题意简述
给定 \(n\) 和 \(2^n\) 个数,初始有一个数\(x=0\),每次操作有一定概率选中 \(i\) ,并将 \(x\) 变成 \(x\ \mathrm{xor}\ i\) ,问将 \(x\) 第一次变为某个数的期望操作次数。
\(n\le 18\)。
算法分析
设 \(E(i)\) 表示 \(x\) 第一次从 \(0\) 变为 \(i\) 的期望步数。
不难得到如下 \(2^n\) 个方程:
\[E(0)=0
\]
\[E(i)=1+\sum_{j \ \mathrm{xor}\ k=i}a_j\cdot E(k)\ ,\ i\neq 0
\]
这个形式是比较标准的 FWT
形式,考虑把 \(E(i)\) 写成生成函数:
\[E=I+E*A
\]
(注:此处的乘号表示的是广义乘法,本题中对应 xor
卷积)
但这个式子对常数项等细节处理并不正确,当我们把两边的所有系数相加,会发现 \(0=2^n\)。
因此实际上是这样的:
\[E+2^n=I+E*A
\]
即
\[E=\frac{I-2^n}{1-A}
\]
这样在 FWT
后点值可以直接相除,即可解决。
最后还有一个问题,把上面求出的 \(E\) 进行 IFWT
后会发现可能不满足 \(E(0)=0\) ,考虑回到最原始的式子:
\[E(i)=1+\sum_{j \ \mathrm{xor}\ k=i}a_j\cdot E(k)\ ,\ i\neq 0
\]
因为 \(\sum a_i=0\) ,所以对所有 \(E(i)\) 同时加上一个常数 \(t\) 不影响正确性,即可调整。
时间复杂度 \(O(n2^n)\) 。
代码实现
#include<bits/stdc++.h>
using namespace std;
#define maxn 1000005
#define maxm 2000005
#define inf 0x3f3f3f3f
#define int long long
#define mod 998244353
#define local
void file(string s){freopen((s+".in").c_str(),"r",stdin);freopen((s+".out").c_str(),"w",stdout);}
template <typename Tp> void read(Tp &x){
int fh=1;char c=getchar();x=0;
while(c>'9'||c<'0'){if(c=='-'){fh=-1;}c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c&15);c=getchar();}x*=fh;
}
int ksm(int b,int p,int Mod=mod){int ret=1;while(p){if(p&1)ret=1ll*ret*b%Mod;b=1ll*b*b%Mod;p>>=1;}return ret;}
int n,m;
int a[maxn],b[maxn],e[maxn];
void fwt(int *f,int typ){
for(int len=1;len<n;len<<=1){
for(int i=0;i<n;i+=(len<<1)){
for(int j=0;j<len;++j){
int l=f[i+j],r=f[i+j+len];
f[i+j]=(l+r)%mod;
f[i+j+len]=(l-r+mod)%mod;
}
}
}
if(typ==-1){
int NN=ksm(n,mod-2);
for(int i=0;i<n;++i)f[i]=1ll*f[i]*NN%mod;
}
}
signed main(){
int sm=0;
read(n);n=(1<<n);
for(int i=0;i<n;++i)read(a[i]),sm=(sm+a[i])%mod;
sm=ksm(sm,mod-2);
for(int i=0;i<n;++i)a[i]=1ll*a[i]*sm%mod;
a[0]=(a[0]-1+mod)%mod;//处理出 1-A
for(int i=0;i<n;++i)b[i]=(mod-1);
b[0]=(b[0]+n+mod)%mod;//处理出 I-2^n
fwt(a,1);fwt(b,1);
for(int i=0;i<n;++i)e[i]=1ll*b[i]*ksm(a[i],mod-2)%mod;//直接点值相除
fwt(e,-1);
for(int i=0;i<n;++i)printf("%lld\n",(e[i]-e[0]+mod)%mod);//调整
return 0;
}