• [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 igoplus j]+1)

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

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

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

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

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

    [{{}E[0],E[1],…,E[2^n-1]{}} igoplus {{}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('
    ');
    	return 0;
    }
    
  • 相关阅读:
    python中list常用的方法
    python登陆代码简单逻辑
    学习Python前言
    Python远程连接Redis
    virtualbox虚拟机之连接本地主机同时可以连接外部网络
    Redis统计访问量方法
    Linux:less and Aix:more
    python之class Meta用法
    pycharm之ctrl+鼠标滚轮调整字体大小
    python之cookie与session
  • 原文地址:https://www.cnblogs.com/xh092113/p/12393170.html
Copyright © 2020-2023  润新知