Atcoder Grand Contest 034 F - RNG and XOR(FWT)

Atcoder 题面传送门 & 洛谷题面传送门

tsc 考试前 A 的题了,结果到现在才写这篇题解……为了 2mol 我已经一周没碰键盘了,现在 2mol 结束算是可以短暂的春天 短暂地卷一会儿 OI 了((

u1s1 写这篇题解的时候我连题都快忘了。。。

首先设 \(b_i=\dfrac{A_i}{\sum\limits_{j=0}^{2^n-1}A_j}\),其次碰到这种期望类的题目我们考虑套路地设 \(p_i\) 表示异或得到 \(i\) 的概率,那么有 \(p_i=\sum\limits_{j=0}^{2^n-1}p_jb_{i\oplus j}+1\)\(p_0=0\)。这个状态定义是存在后效性的,而高斯消元 \(8^n\) 又显然会超时,因此考虑从这个转移式本身入手进行优化。注意到这个转移式长得有点像异或卷积,因此考虑用异或卷积的形式将这个式子写出来,即 \(p=p\times b+I\),其中 \(\forall i,I_i=1\),异常好理解。

不过事实上这个式子是有个 bug 的,\(p_0=0\),而将 \(p_0\) 带到右边去就不一定等于 \(0\) 了,也就是说左边实际上要加上一个常数 \(X\),即 \(p+X=p\times b+I(X\in\mathbb{R})\)(事实上很多生成函数的题也需要注意常数项的问题,比如说斐波那契数列,这里就不再赘述了)。怎么求这个 \(X\) 的值呢?这里又是一个套路,将左边每一项都变成它的系数和,相当于多项式里的令 \(x=1\),那么可以得到 \((\sum\limits_{i=0}^{2^n-1}p_i)+X=(\sum\limits_{i=0}^{2^n-1}p_i)\times (\sum\limits_{i=0}^{2^n-1}b_i)+(\sum\limits_{i=0}^{2^n-1}I_i)\),而显然 \(\sum\limits_{i=0}^{2^n-1}b_i=1\),故 \(X=\sum\limits_{i=0}^{2^n-1}I_i=2^n\),因此 \(p+2^n=p\times b+I\),简单移个项可得 \(2^n-I=p\times(b-1)\),故 \(p=\dfrac{2^n-I}{b-1}\),把上下两个幂级数 FWTxor 一下相除再 FWTxor 回去即可得到 \(p\)

还有一个小问题,就是在求逆元的时候有可能会出现分母为 \(0\) 的情况。事实上,由于 \(A_i\le 1000\),因此从所有 \(b_i\) 中任意选出一些出来它们的分子分母都 \(<998244353\),记 \(F=\text{FWT}(b-1)\),那么必然有 \(F_i\le 0(i\ne 0)\),而 \(F_0=-1+\sum\limits_{i=0}^{2^n-1}b_i=0\),因此出错的只可能是 \(F_0\)。不过这个问题很好解决,我们已知 \(p_0=0\),而显然对于一个幂级数 \(F\) FWTxor 后得到的幂级数 \(G\),常数项上加上一个常数 \(C\) 对于 \(F\) 的作用效果就是每一项都加上 \(C\),因此我们只需输出 \(p_i-p_0\) 即可。

const int MAXP=1<<18;
const int MOD=998244353;
int qpow(int x,int e=MOD-2){
	int ret=1;
	for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
	return ret;
}
int n,a[MAXP+5],b[MAXP+5],c[MAXP+5];
void FWTxor(int *a,int len,int type){
	for(int i=2;i<=len;i<<=1)
		for(int j=0;j<len;j+=i)
			for(int k=0;k<(i>>1);k++){
				int X=a[j+k],Y=a[(i>>1)+j+k];
				a[j+k]=1ll*(X+Y)*type%MOD;
				a[(i>>1)+j+k]=1ll*(X-Y+MOD)*type%MOD;
			}
}
int main(){
	scanf("%d",&n);n=1<<n;int sum=0;
	for(int i=0;i<n;i++) scanf("%d",&a[i]),sum+=a[i];sum=qpow(sum);
	for(int i=0;i<n;i++) a[i]=1ll*a[i]*sum%MOD;a[0]=(a[0]-1+MOD)%MOD;
	for(int i=0;i<n;i++) b[i]=MOD-1;b[0]=(b[0]+n)%MOD;
	FWTxor(a,n,1);FWTxor(b,n,1);
	for(int i=0;i<n;i++) c[i]=1ll*b[i]*qpow(a[i])%MOD;
	FWTxor(c,n,MOD+1>>1);
	for(int i=0;i<n;i++) printf("%d\n",(c[i]-c[0]+MOD)%MOD);
	return 0;
}
posted @ 2021-06-01 18:43  tzc_wk  阅读(46)  评论(0编辑  收藏  举报