ZJOI2019 Day1 题解
想要继续向前,就从克服内心的恐惧开始。
麻将
题意
在麻将中,我们称点数连续的三张牌或三张点数一样的成为面子,称两张点数一样的牌为对子。一副十四张麻将牌的胡牌条件是可以分成四个面子和一个对子或者分成七个互不相同的对子。现在规定麻将牌的点数为
题解
首先对题目要求的期望进行转化。假设
那么考虑将概率转化为更好算的方案数,最后只要令
那么如何在给定一副牌的情况下,判段它是否存在一个子集是否可以胡牌呢?
我们可以用一个简单的
考虑组成了
考虑这个状态的转移,可以枚举点数为
我们可以发现,状态的转移和
最终我们可以发现,合法的状态一共有
既然状态数这么少,那么我们不妨预处理好所有的状态对应的转移。为了减小常数,我们不妨对每个不同的状态标一下号。于是我们就可以预处理数组
最终我们就可以计算数组
我们设
这样的关系,左边的系数的组合意义为,确定哪些牌作为拿到的牌,并决定这些牌的相对顺序,最后用隔板法插入到已有的牌中。
最终我们就得到了答案。复杂度是
代码:
/*
There is no end though there is a start in space. ---Infinity.
It has own power, it ruins, and it goes though there is a start also in the star. ---Finite.
Only the person who was wisdom can read the most foolish one from the history.
The fish that lives in the sea doesn't know the world in the land.
It also ruins and goes if they have wisdom.
It is funnier that man exceeds the speed of light than fish start living in the land.
It can be said that this is an final ultimatum from the god to the people who can fight.
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
using std::max;
using std::min;
using std::map;
using std::queue;
const int mod=998244353;
inline int add(int a,int b)
{
return (a+=b)>=mod?a-mod:a;
}
inline int sub(int a,int b)
{
return (a-=b)<0?a+mod:a;
}
inline int mul(int a,int b)
{
return (long long)a*b%mod;
}
inline int qpow(int a,int b)
{
int res=1;
for(;b;a=mul(a,a),b>>=1)
if(b&1)
res=mul(res,a);
return res;
}
struct epc
{
int a[2][3][3];
int cnt;
epc()
{
memset(a,-1,sizeof(a));
a[0][0][0]=0;
cnt=0;
return;
}
inline bool operator<(const epc &th) const
{
register int i,j,k;
for(i=0;i<2;i++)
for(j=0;j<3;j++)
for(k=0;k<3;k++)
if(a[i][j][k]!=th.a[i][j][k])
return a[i][j][k]<th.a[i][j][k];
return cnt<th.cnt;
}
inline epc trans(int c)
{
register int i,j,k;
epc res;
memset(res.a,-1,sizeof(res.a));
res.cnt=cnt+(c>=2);
res.cnt=min(res.cnt,7);
for(i=0;i<3;i++)
for(j=0;j<3;j++)
for(k=0;k<3;k++)
if(k+i+j<=c)
{
if(a[0][k][i]!=-1)
res.a[0][i][j]=max(res.a[0][i][j],a[0][k][i]+k+(c-(k+i+j))/3);
if(a[1][k][i]!=-1)
res.a[1][i][j]=max(res.a[1][i][j],a[1][k][i]+k+(c-(k+i+j))/3);
}
if(c>=2)
for(i=0;i<3;i++)
for(j=0;j<3;j++)
for(k=0;k<3;k++)
if(k+i+j<=c-2&&a[0][k][i]!=-1)
res.a[1][i][j]=max(res.a[1][i][j],a[0][k][i]+k+(c-2-(k+i+j))/3);
for(i=0;i<2;i++)
for(j=0;j<3;j++)
for(k=0;k<3;k++)
res.a[i][j][k]=min(res.a[i][j][k],4);
return res;
}
inline bool win()
{
bool f=0;
register int i,j;
for(i=0;i<3;i++)
for(j=0;j<3;j++)
f|=a[1][i][j]==4;
f|=cnt==7;
return f;
}
};
const int M=4005;
map<epc,int> id;
int tot;
int trans[M][5];
bool book[M];
queue<epc> Q;
inline void bfs()
{
epc x,y;
register int i;
id[epc()]=++tot;book[tot]=epc().win();Q.push(epc());
while(!Q.empty())
{
x=Q.front();Q.pop();
for(i=0;i<5;i++)
{
y=x.trans(i);
if(id.find(y)==id.end())
id[y]=++tot,book[tot]=y.win(),Q.push(y);
trans[id[x]][i]=id[y];
}
}
return;
}
const int N=105;
int n,tans;
int cnt[N];
int dp[2][M][N*4];
int fact[N*4],ifact[N*4];
int ans[N*4];
inline int choose(int n,int m)
{
return mul(fact[n],mul(ifact[m],ifact[n-m]));
}
signed main()
{
bfs();
int x;
register int i,j,k,c;
scanf("%d",&n);
for(i=0;i<13;i++)
scanf("%d%*d",&x),cnt[x]++;
fact[0]=1;
for(i=1;i<=4*n;i++)
fact[i]=mul(fact[i-1],i);
ifact[4*n]=qpow(fact[4*n],mod-2);
for(i=4*n-1;i>=0;i--)
ifact[i]=mul(ifact[i+1],i+1);
dp[0][1][0]=1;
for(i=0;i<n;i++)
{
for(j=1;j<=tot;j++)
for(k=0;k<=4*(i+1);k++)
dp[(i+1)&1][j][k]=0;
for(j=1;j<=tot;j++)
for(k=0;k<=4*i;k++)
if(dp[i&1][j][k])
for(c=0;c<=4-cnt[i+1];c++)
dp[(i+1)&1][trans[j][cnt[i+1]+c]][k+c]=add(dp[(i+1)&1][trans[j][cnt[i+1]+c]][k+c],mul(dp[i&1][j][k],mul(mul(choose(4-cnt[i+1],c),fact[c]),choose(k+c,c))));
}
for(j=1;j<=tot;j++)
if(!book[j])
for(k=0;k<=4*n-13;k++)
ans[k]=add(ans[k],dp[n&1][j][k]);
for(k=0;k<=4*n-13;k++)
ans[k]=mul(ans[k],qpow(mul(choose(4*n-13,k),fact[k]),mod-2));
for(k=0;k<=4*n-13;k++)
tans=add(tans,ans[k]);
printf("%d\n",tans);
return 0;
}
线段树
题意
你有一棵按照常规方法建立的
题解
不妨将操作一看作是每次有
于是我们对于线段树的每个节点,维护两个值
考虑每一次修改对每个节点的影响。我们可以将节点分为
我们分别考虑影响:
可以发现,前
于是直接实现这些操作就可以了。复杂度
代码:
/*
There is no end though there is a start in space. ---Infinity.
It has own power, it ruins, and it goes though there is a start also in the star. ---Finite.
Only the person who was wisdom can read the most foolish one from the history.
The fish that lives in the sea doesn't know the world in the land.
It also ruins and goes if they have wisdom.
It is funnier that man exceeds the speed of light than fish start living in the land.
It can be said that this is an final ultimatum from the god to the people who can fight.
*/
#include<cstdio>
const int mod=998244353,inv2=(mod+1)/2;
inline int add(int a,int b)
{
return (a+=b)>=mod?a-mod:a;
}
inline int sub(int a,int b)
{
return (a-=b)<0?a+mod:a;
}
inline int mul(int a,int b)
{
return (long long)a*b%mod;
}
const int N=1e5+5;
int sum;
int p[N<<2],q[N<<2],k[N<<2],b[N<<2];
inline void push_down(int x)
{
q[x<<1]=add(mul(k[x],q[x<<1]),b[x]);
b[x<<1]=add(mul(k[x],b[x<<1]),b[x]);
k[x<<1]=mul(k[x],k[x<<1]);
q[x<<1|1]=add(mul(k[x],q[x<<1|1]),b[x]);
b[x<<1|1]=add(mul(k[x],b[x<<1|1]),b[x]);
k[x<<1|1]=mul(k[x],k[x<<1|1]);
k[x]=1;b[x]=0;
return;
}
void modify(int x,int lp,int rp,int l,int r)
{
if(lp==l&&rp==r)
{
sum=sub(sum,p[x]);
p[x]=add(mul(inv2,p[x]),inv2);
q[x]=add(mul(inv2,q[x]),inv2);
b[x]=add(mul(inv2,b[x]),inv2);
k[x]=mul(inv2,k[x]);
sum=add(sum,p[x]);
return;
}
push_down(x);
int mid=(lp+rp)>>1;
sum=sub(sum,p[x]);
p[x]=mul(inv2,p[x]);
q[x]=mul(inv2,q[x]);
sum=add(sum,p[x]);
if(r<=mid)
{
modify(x<<1,lp,mid,l,r);
sum=sub(sum,p[x<<1|1]);
p[x<<1|1]=mul(inv2,add(p[x<<1|1],q[x<<1|1]));
sum=add(sum,p[x<<1|1]);
}
else if(l>=mid+1)
{
sum=sub(sum,p[x<<1]);
p[x<<1]=mul(inv2,add(p[x<<1],q[x<<1]));
sum=add(sum,p[x<<1]);
modify(x<<1|1,mid+1,rp,l,r);
}
else
modify(x<<1,lp,mid,l,mid),modify(x<<1|1,mid+1,rp,mid+1,r);
return;
}
void build(int x,int lp,int rp)
{
k[x]=1;
if(lp==rp)
return;
int mid=(lp+rp)>>1;
build(x<<1,lp,mid);
build(x<<1|1,mid+1,rp);
return;
}
int n,m;
signed main()
{
int opt,l,r,w=1;
scanf("%d%d",&n,&m);
build(1,1,n);
while(m--)
if(scanf("%d",&opt),opt==1)
scanf("%d%d",&l,&r),modify(1,1,n,l,r),w=mul(w,2);
else
printf("%d\n",mul(w,sum));
return 0;
}
Minimax搜索
题意
有一颗
题解
我们首先考虑如何计算对于
那么首先考虑如何对于一个特定的
首先我们可以通过一遍
我们可以发现,想要修改根的权值,有让它变大和让它变小两种选择。对于让它变大的情况,与其让它变得非常大,不如让它变成
由于两者是相似的,因此我们以计算有多少个只包含
为了在计算中更加方便,我们将方案数转化为概率,令每个
首先是
假如这是一个深度为奇数的点,那么它的权值可以
否则,这是一个深度为偶数的点,那么它的权值可以
我们发现两个转移的形式比较相似,于是考虑进一步进行简化。我们可以设
变得更加简单。于是我们可以轻松的在
现在考虑快速求出所有的答案。我们发现只有当叶子节点的答案改变了,最终答案才会改变。而对于每一个叶子节点,在
代码:
/*
There is no end though there is a start in space. ---Infinity.
It has own power, it ruins, and it goes though there is a start also in the star. ---Finite.
Only the person who was wisdom can read the most foolish one from the history.
The fish that lives in the sea doesn't know the world in the land.
It also ruins and goes if they have wisdom.
It is funnier that man exceeds the speed of light than fish start living in the land.
It can be said that this is an final ultimatum from the god to the people who can fight.
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<functional>
#include<vector>
using std::max;
using std::min;
using std::sort;
using std::greater;
using std::vector;
const int mod=998244353,inv2=(mod+1)/2;
inline int add(int a,int b)
{
return (a+=b)>=mod?a-mod:a;
}
inline int sub(int a,int b)
{
return (a-=b)<0?a+mod:a;
}
inline int mul(int a,int b)
{
return (long long)a*b%mod;
}
inline int qpow(int a,int b)
{
int res=1;
for(;b;a=mul(a,a),b>>=1)
if(b&1)
res=mul(res,a);
return res;
}
const int N=2e5+5;
struct zint
{
int v,cnt;
zint(int x=0)
{
if(x)
v=x,cnt=0;
else
v=1,cnt=1;
return;
}
zint(int _v,int _cnt)
{
v=_v;cnt=_cnt;
return;
}
inline int to_int()
{
if(cnt)
return 0;
return v;
}
inline zint operator*(const zint &th)
{
return zint(mul(v,th.v),cnt+th.cnt);
}
inline zint operator/(const zint &th)
{
return zint(mul(v,qpow(th.v,mod-2)),cnt-th.cnt);
}
};
struct epc
{
int k,b;
epc(int _k=1,int _b=0)
{
k=_k;b=_b;
return;
}
inline epc operator*(const epc &th)
{
return epc{mul(th.k,k),add(mul(th.b,k),b)};
}
}I=epc();
namespace zkw
{
int M;
epc sgt[N<<2];
inline void init(int n)
{
for(M=1;M<=n;M<<=1);
return;
}
inline void modify(int x,epc k)
{
sgt[M+x]=k;
for(x=(M+x)>>1;x;x>>=1)
sgt[x]=sgt[x<<1]*sgt[x<<1|1];
return;
}
inline epc query(int l,int r)
{
epc lres=I,rres=I;
for(l=M+l-1,r=M+r+1;l^r^1;l>>=1,r>>=1)
lres=(l&1)?lres:lres*sgt[l^1],rres=(r&1)?sgt[r^1]*rres:rres;
return lres*rres;
}
}
int n,L,R,idx;
int isl[N],key[N];
int father[N],hson[N],size[N],deep[N];
int id[N],di[N],top[N],bot[N];
zint dp[N];
vector<int> e[N];
vector<int> low,ex,high;
int ansf[N],ansg[N],ans[N];
void dfs(int x,int father,int d)
{
isl[x]=1;
if(d&1)
key[x]=0;
else
key[x]=n+1;
for(int y:e[x])
if(y!=father)
{
isl[x]=0;
dfs(y,x,d+1);
if(d&1)
key[x]=max(key[x],key[y]);
else
key[x]=min(key[x],key[y]);
}
if(isl[x])
key[x]=x;
return;
}
void dfs1(int x)
{
size[x]=1;
deep[x]=deep[father[x]]+1;
for(int y:e[x])
if(y!=father[x])
father[y]=x,dfs1(y),size[x]+=size[y],hson[x]=(size[y]>size[hson[x]]?y:hson[x]);
return;
}
void dfs2(int x,int root)
{
register int i;
id[x]=++idx;di[idx]=x;top[x]=root;
if(hson[x])
dfs2(hson[x],root);
else
for(i=x;i!=father[top[x]];i=father[i])
bot[i]=x;
for(int y:e[x])
if(y!=father[x]&&y!=hson[x])
dfs2(y,y);
return;
}
signed main()
{
int x,y,ptr,cntl=0;
epc e;
register int i;
scanf("%d%d%d",&n,&L,&R);
for(i=1;i<=n-1;i++)
scanf("%d%d",&x,&y),::e[x].push_back(y),::e[y].push_back(x);
dfs(1,0,1);
for(i=1;i<=n;i++)
if(isl[i])
{
cntl++;
if(i<key[1])
low.push_back(i);
else if(i==key[1])
ex.push_back(i);
else
high.push_back(i);
}
dfs1(1);
dfs2(1,1);
zkw::init(n);
for(i=1;i<=n;i++)
if(isl[i])
dp[i]=(key[i]>key[1])?((deep[i]&1)?0:1):((deep[i]&1)?1:0);
else
dp[i]=1;
for(i=n;i>=1;i--)
{
zkw::modify(i,epc(sub(0,dp[di[i]].to_int()),dp[di[i]].to_int()));
if(i>1&&top[di[i]]==di[i])
e=zkw::query(i,id[bot[di[i]]]),dp[father[di[i]]]=dp[father[di[i]]]*sub(1,e.b);
}
sort(low.begin(),low.end(),greater<int>());
ptr=0;
for(i=0;i<n;i++)
{
while(ptr<(int)low.size()&&key[1]+1-low[ptr]<=i)
{
x=low[ptr];
dp[x]=inv2;
while(x)
{
e=zkw::query(id[top[x]],id[bot[x]]);
if(father[top[x]])
dp[father[top[x]]]=dp[father[top[x]]]/sub(1,e.b);
zkw::modify(id[x],epc(sub(0,dp[x].to_int()),dp[x].to_int()));
e=zkw::query(id[top[x]],id[bot[x]]);
if(father[top[x]])
dp[father[top[x]]]=dp[father[top[x]]]*sub(1,e.b);
x=father[top[x]];
}
ptr++;
}
e=zkw::query(1,id[bot[1]]);
ansf[i]=sub(1,e.b);
}
ansf[n]=1;
for(i=1;i<=n;i++)
if(isl[i])
dp[i]=(key[i]<key[1])?((deep[i]&1)?1:0):((deep[i]&1)?0:1);
else
dp[i]=1;
for(i=n;i>=1;i--)
{
zkw::modify(i,epc(sub(0,dp[di[i]].to_int()),dp[di[i]].to_int()));
if(i>1&&top[di[i]]==di[i])
e=zkw::query(i,id[bot[di[i]]]),dp[father[di[i]]]=dp[father[di[i]]]*sub(1,e.b);
}
sort(high.begin(),high.end());
ptr=0;
for(i=0;i<n;i++)
{
while(ptr<(int)high.size()&&high[ptr]-key[1]+1<=i)
{
x=high[ptr];
dp[x]=inv2;
while(x)
{
e=zkw::query(id[top[x]],id[bot[x]]);
if(father[top[x]])
dp[father[top[x]]]=dp[father[top[x]]]/sub(1,e.b);
zkw::modify(id[x],epc(sub(0,dp[x].to_int()),dp[x].to_int()));
e=zkw::query(id[top[x]],id[bot[x]]);
if(father[top[x]])
dp[father[top[x]]]=dp[father[top[x]]]*sub(1,e.b);
x=father[top[x]];
}
ptr++;
}
e=zkw::query(1,id[bot[1]]);
ansg[i]=e.b;
}
ansg[n]=1;
for(i=0;i<=n;i++)
ans[i]=sub(1,mul(sub(1,ansf[i]),sub(1,ansg[i])));
x=qpow(2,cntl-1);
for(i=0;i<=n;i++)
ans[i]=mul(ans[i],x);
for(i=1;i<=n;i++)
ans[i]=add(ans[i],x);
for(i=n;i>=1;i--)
ans[i]=sub(ans[i],ans[i-1]);
ans[n]=sub(ans[n],1);
for(i=L;i<=R;i++)
printf("%d ",ans[i]);
putchar('\n');
return 0;
}