[AGC034F]RNG and XOR(FWT)

题面

https://atcoder.jp/contests/agc034/tasks/agc034_f

题解

前置知识

首先设得到i的期望步数为E[i]。容易看出E[0]=0以及对i>0,\(E[i]=\sum_{j=0}^{2^n-1}p[j]E[i \bigoplus j]+1\)

将1移到左边,可以得到\(E[i]-1=\sum_{j=0}^{2^n-1}p[j]E[i \bigoplus j]\)。①

发现这个式子很像一个异或卷积,它可以描述为三个数组之间的关系:

\[{\{}E[0],E[1],…,E[2^n-1]{\}} \bigoplus {\{}p[0],p[1],…,p[2^n-1]{\}}={\{}?,E[1]-1,E[2]-1,…,E[2^n-1]-1{\}} \]

其中\(\bigoplus\)表示两个数列之间的异或卷积。?的存在是因为E[0]并不满足①式。不过没关系,我们可以算出?是什么。中间的p数组满足一个性质——它们的和是1。所以,左边所有数的和一定等于右边所有数的和。所以\(?=2^n-1\)

之后,我们将中间数组的第一项-=1,就可以完美地消掉右边的E:

\[{\{}E[0],E[1],…,E[2^n-1]{\}} \bigoplus {\{}p[0]-1,p[1],…,p[2^n-1]{\}}={\{}2^n-1,-1,-1,…,-1{\}} \]

看上去我们已经做完了,因为中间和右边的数组都已知,求左边还不容易吗?只需要把右边进行FWT,再与中间FWT后的结果的逆元逐项相乘,再UFWT就是左边了。

问题就出在“逆元”上。

0是没有逆元的,而我们仔细思考,发现中间FWT以后,是会出现0的!出现0的就是第一项,这一项在FWT以后会变成\(p[0]-1+p[1]+…+p[2^n-1]\),也就是0。其他项可以证明不是0。核对右边的第一项,FWT以后变成\(2^n-1-1-1…-1\)也是0。感觉我们少了个条件,做不下去了。

并不是!其实E中也有一项已知的,就是\(E[0]=0\)。它可是有巨大的用途的。根据我们的推导,\({\{}E[0],E[1],…,E[2^n-1]{\}}\)FWT以后的结果是\({\{}q[0],q[1],…,q[2^n-1]{\}}\),这个q的1到\(2^n-1\)都已知了,只有q[0]未知。左边是E[0]已知,其他未知。根据异或卷积的公式,一定存在下列关系

\[\frac{\frac{\frac{\frac{q[0]+q^{(1)}[2^0]}{2}+q^{(2)}[2^1]}{2}+q^{(3)}[2^2]}{…}+q^{(n)}[2^{n-1}]}{2}=E[0]=0 \]

其中\(q^{(i)}[j]\)表示FWT中进行第i轮循环时,q[j]的值。由于\(q^{(1)}[2^0],q^{(2)}[2^1],…,q^{(n)}[2^{n-1}]\)这些值都与q[0]线性无关,所以简化为

\[\frac{q[0]}{2^n}+M=0 \]

这个M极其繁琐,怎么求?

强行代q[0]=0,算一个UFWT,算出来\(E[0]=k\)。这代表了\(\frac{0}{2^n}+M=k\),M自然求出。

至此,q[0]求出,本题结束。总时间复杂度\(O(n2^n)\)

代码

#include<bits/stdc++.h>

using namespace std;

#define ll long long
#define rg register
#define In inline

const int N = 262144;
const ll mod = 998244353;
const ll iv2 = 499122177;

In ll read(){
	ll s = 0,ww = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-')ww = -1;ch = getchar();}
	while('0' <= ch && ch <= '9'){s = 10 * s + ch - '0';ch = getchar();}
	return s * ww;
}

In void write(ll x){
	if(x < 0)putchar('-'),x = -x;
	if(x > 9)write(x / 10);
	putchar('0' + x % 10);
}

namespace ModCalc{
	In void Inc(ll &x,ll y){
		x += y;if(x >= mod)x -= mod;
	}
	In void Dec(ll &x,ll y){
		x -= y;if(x < 0)x += mod;
	}
	In ll Add(ll x,ll y){
		Inc(x,y);return x;
	}
	In ll Sub(ll x,ll y){
		Dec(x,y);return x;
	}
}
using namespace ModCalc;

In ll power(ll a,ll n){
	ll s = 1,x = a;
	while(n){
		if(n & 1)s = s * x % mod;
		x = x * x % mod;
		n >>= 1;
	}
	return s;
}

ll n,deg;
ll p[N+5],q[N+5],temp[N+5];

In void calc(ll &x,ll &y,int opt){
	if(opt == 1){
		ll X = Add(x,y),Y = Sub(x,y);
		x = X,y = Y;
	}
	else{
		ll X = Add(x,y) * iv2 % mod,Y = Sub(x,y) * iv2 % mod; 
		x = X,y = Y;
	}
}

void FWT(ll a[],ll deg,int opt){
	for(rg int n = 2;n <= deg;n <<= 1){
		int m = n >> 1;
		for(rg int i = 0;i < deg;i += n){
			for(rg int j = 0;j < m;j++)calc(a[i+j],a[i+j+m],opt);
		}
	}
}

int main(){
	n = read();
	deg = 1ll << n;
	ll s = 0;
	for(rg int i = 0;i < deg;i++)p[i] = read(),s += p[i];
	ll iv = power(s,mod - 2);
	for(rg int i = 0;i < deg;i++)p[i] = p[i] * iv % mod;
	Dec(p[0],1);
	FWT(p,deg,1);
	for(rg int i = 0;i < deg;i++)q[i] = (i == 0 ? deg - 1 : -1);
	FWT(q,deg,1);
	for(rg int i = 1;i < deg;i++)q[i] = q[i] * power(p[i],mod - 2) % mod;
	memcpy(temp,q,sizeof(temp));
	FWT(temp,deg,-1);
	Dec(q[0],temp[0] * deg % mod);
	FWT(q,deg,-1);
	for(rg int i = 0;i < deg;i++)write(q[i]),putchar('\n');
	return 0;
}
posted @ 2020-10-04 19:52  coder66  阅读(135)  评论(0编辑  收藏  举报