AGC034F RNG and XOR

原题
翻译


正解

思考的时候没有得到除了高斯消元以外的思路……
原来是一道板子题……

p i p_i pi为选 i i i的概率, e i e_i ei i i i第一次变成 0 0 0的期望步数(显然和 0 0 0第一次变成 i i i一样)
显然可以列出式子:
e i = 1 + ∑ p j e i ⨁ j e_i=1+\sum p_je_{i\bigoplus j} ei=1+pjeij
e 0 = 0 e_0=0 e0=0

用集合幂级数来表示 e e e p p p,分别记为 E E E P P P
乘法定义为异或运算。
于是有 E + c = I + P ∗ E E+c=I+P*E E+c=I+PE
I I I表示全部位置都是 1 1 1的集合幂级数。
c c c是用来修正 E 0 E_0 E0的常数。

“集合幂级数”只是个高大上的名词……
类似于多项式,一个集合幂级数表示成这个样子: F = ∑ a i x i F=\sum a_ix^i F=aixi
和多项式不同的是,这里按照需要定义了 x a ∗ x b x^a*x^b xaxb的结果。比如,如果乘法定义为异或,那么 x a ∗ x b = x a ⨁ b x^a*x^b=x^{a \bigoplus b} xaxb=xab

S ( F ) S(F) S(F)表示 ∑ F i \sum F_i Fi
于是有 S ( E ) + c = 2 n + S ( P ) S ( E ) S(E)+c=2^n+S(P)S(E) S(E)+c=2n+S(P)S(E)

S ( P ∗ E ) = S ( P ) S ( E ) S(P*E)=S(P)S(E) S(PE)=S(P)S(E)
因为 P P P中每个数和 E E E中每个数都可以做出贡献。

由于 S ( P ) = 1 S(P)=1 S(P)=1,所以 c = 2 n c=2^n c=2n
所以 E + 2 n = I + P ∗ E E+2^n=I+P*E E+2n=I+PE
移项得 2 n − I = ( P − 1 ) ∗ E 2^n-I=(P-1)*E 2nI=(P1)E
F W T FWT FWT F W T ( 2 n − I ) = F W T ( P − 1 ) F W T ( E ) FWT(2^n-I)=FWT(P-1)FWT(E) FWT(2nI)=FWT(P1)FWT(E)
考虑计算 F W T ( E ) i FWT(E)_i FWT(E)i。可以只有 i = 0 i=0 i=0 F W T ( P − 1 ) = 0 FWT(P-1)=0 FWT(P1)=0
i > 0 i>0 i>0时就暴力算出, i = 0 i=0 i=0时根据 E 0 = 0 E_0=0 E0=0手玩出 F W T ( E ) 0 FWT(E)_0 FWT(E)0的取值。

算出 F W T ( E ) FWT(E) FWT(E)之后 I F W T IFWT IFWT回去即可。


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 18
#define ll long long
#define mo 998244353
ll qpow(ll x,int y=mo-2){
	ll r=1;
	for (;y;y>>=1,x=x*x%mo)
		if (y&1)
			r=r*x%mo;
	return r;
}
int n;
int a[1<<N],p[1<<N],e[1<<N],f[1<<N];
void fwt(int F[]){
	for (int i=1;i<1<<n;i<<=1)
		for (int j=0;j<1<<n;j+=i<<1)
			for (int k=j;k<j+i;++k){
				int x=F[k],y=F[k+i];
				F[k]=(x+y)%mo;
				F[k+i]=(x-y+mo)%mo;
			}
}
int main(){
	scanf("%d",&n);
	ll sum=0;
	for (int i=0;i<1<<n;++i)
		scanf("%d",&a[i]),sum+=a[i];
	sum=qpow(sum);
	for (int i=0;i<1<<n;++i)
		p[i]=a[i]*sum%mo;
	
	for (int i=0;i<1<<n;++i)
		f[i]=-1+mo;
	(f[0]+=1<<n)%=mo;
	fwt(f);
	(p[0]=p[0]-1+mo)%=mo;
	fwt(p);
	
	sum=0;
	for (int i=1;i<1<<n;++i){
		e[i]=(ll)f[i]*qpow(p[i])%mo;
		sum+=e[i];
	}
	e[0]=(mo-sum%mo)%mo;
	fwt(e);
	ll inv=qpow(1<<n);
	for (int i=0;i<1<<n;++i)
		e[i]=e[i]*inv%mo;
	for (int i=0;i<1<<n;++i)
		printf("%d\n",e[i]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值