题解 CF1411G No Game No Life
首先,我们可以在
题目转化为给定一个数组
- 在
[1,n+1] 中等概率随机一个整数p 。若p\le n ,进行操作 2;否则,进行操作 3。 -
- 游戏结束,若
v=0 则输,否则赢。
求赢的概率。
设
这里我们要证明
证明需要先了解 FWT 的一个性质, FWT 是线性变换且每一项系数都为
因为
知道这点就可以直接做了,求逆的方式就是点值每一项都求逆,时间复杂度就是 FWT 的复杂度。
不过 SG 值的值域是
#include<cstdio>
#include<algorithm>
#include<vector>
int const mod=998244353,inv2=(mod+1)/2;
std::vector<int> v[100010],p[100010];
int n,m,d[100010],a[100010],f[600],lim;
int pow(int x,int y){
int res=1;
while(y){
if(y&1) res=1ll*res*x%mod;
x=1ll*x*x%mod,y>>=1;
}
return res;
}
void fwt(int *a){
for(int mid=1;mid<lim;mid<<=1)
for(int r=mid<<1,j=0;j<lim;j+=r)
for(int k=0;k<mid;k++){
int x=a[k+j],y=a[mid+j+k];
a[k+j]=(x+y)%mod,a[mid+j+k]=(x-y+mod)%mod;
}
}
void ifwt(int *a){
for(int mid=1;mid<lim;mid<<=1)
for(int r=mid<<1,j=0;j<lim;j+=r)
for(int k=0;k<mid;k++){
int x=a[k+j],y=a[mid+j+k];
a[k+j]=1ll*(x+y)*inv2%mod,a[mid+j+k]=1ll*inv2*(x-y+mod)%mod;
}
}
int main(){
scanf("%d%d",&n,&m);
int lm=pow(n+1,mod-2);
for(int i=1,x,y;i<=m;i++)scanf("%d%d",&x,&y),v[y].push_back(x),++d[x];
static int que[100010];
int *hd=que,*tl=que;
for(int i=1;i<=n;i++) if(!d[i]) *tl++=i;
while(hd!=tl){
int x=*hd++;
std::sort(p[x].begin(),p[x].end());
p[x].erase(std::unique(p[x].begin(),p[x].end()),p[x].end());
for(a[x]=0;a[x]<(int)p[x].size()&&p[x][a[x]]==a[x];a[x]++);
lim=std::max(a[x],lim);
f[a[x]]=(f[a[x]]-lm+mod)%mod;
for(auto u:v[x]){
p[u].push_back(a[x]);
if(!--d[u]) *tl++=u;
}
}
if(lim==0) lim=2;
else do lim+=lim&-lim; while((lim&-lim)!=lim);
f[0]=(f[0]+1)%mod;
for(int i=0;i<lim;i++) f[i]=1ll*f[i]*(n+1)%mod;
fwt(f);
for(int i=0;i<lim;i++) f[i]=pow(f[i],mod-2);
ifwt(f);
printf("%d\n",(1-f[0]+mod)%mod);
return 0;
}