其他分享
首页 > 其他分享> > 2020-2021 “Orz Panda” Cup Programming Contest E. Encryption of Orz Pandas(生成函数,FFT)

2020-2021 “Orz Panda” Cup Programming Contest E. Encryption of Orz Pandas(生成函数,FFT)

作者:互联网

https://codeforces.com/gym/102870
题意:

给定数组a,定义一次操作为对a求异或前缀和数组,求出的数组作为新的a。求a进行k次操作后的值

\(n<=1e5,a_i<=2^{17},k<=1e18\)

解法:

找规律可以发现,不停进行求前缀和操作,a的值会循环变化,循环节是>=n的最小2的幂次,这样就可以将k减小至1e5的级别。

异或操作对于不同数位之间是没有影响的,因此可以将a数组的各数位分离,对于一个数位的数组进行求异或前缀和,就相当于求前缀和最后模2。而对一个长度为n+1的数组求前缀和,可以转化为生成函数来考虑,相当于乘一个生成函数\((1+x+x^2+...+x^n)\),题目要求做k次操作,则相当于对原数组乘一个\((1+x+x^2+...+x^n)^k\),后者可以用FFT套快速幂快速求出,因为最后答案只要取前n+1位,每次多项式相乘只要保留前n+1位即可。

#include<bits/stdc++.h>
typedef long long ll;
typedef double db;
using namespace std;
const db pi=acos(-1.0);
struct cp{
	db x,y;
	cp(){x=y=0;}
	cp(db xx,db yy){x=xx,y=yy;}
};
cp operator + (cp a,cp b){
	return cp(a.x+b.x,a.y+b.y);
}cp operator - (cp a,cp b){
	return cp(a.x-b.x,a.y-b.y);
}cp operator * (cp a,cp b){
	return cp(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);
}
inline void FFT(int n,vector<cp> &a,int t,vector<int> &rev){//迭代版FFT
	for(int i=0;i<=n-1;i++)
		if(i<rev[i])swap(a[i],a[rev[i]]);
	for(int len=1;len<=(n>>1);len<<=1){
		cp w1(cos(pi/len),t*sin(pi/len));
		for(int i=0;i<=n-(len<<1);i+=(len<<1)){
			cp w(1,0);
			for(int j=0;j<=len-1;j++){
				cp x=a[i+j],y=w*a[i+j+len];
				a[i+j]=x+y,a[i+j+len]=x-y;
				w=w*w1;
			}
		}
	}
}
void solve(vector<int>&a,vector<int>&b,vector<int>&res){
    int n,m;
	n=a.size()-1,m=b.size()-1;
	int k=1,ci=0;
	while(k<=n+m)k<<=1,ci++;
	vector<int> rev(k+1,0);
	vector<cp> f(k+1),g(k+1);
	for(int i=0;i<=n;i++)f[i].x=a[i];
	for(int i=0;i<=m;i++)g[i].x=b[i];
	for(int i=1;i<=k-1;i++)//二进制翻转
		rev[i]=(rev[i>>1]>>1)|((i&1)<<(ci-1));
	FFT(k,f,1,rev);
	FFT(k,g,1,rev);
	for(int i=0;i<=k;i++)
		f[i]=f[i]*g[i];
	FFT(k,f,-1,rev);
	res.resize(n+m+1);
	for(int i=0;i<=n+m;i++)
        res[i]=((int)(f[i].x/k+0.5))&1;
}
void m2(vector<int>&a){
    int n,m;
	n=m=a.size()-1;
	int k=1,ci=0;
	while(k<=n+m)k<<=1,ci++;
	vector<int> rev(k+1,0);
	vector<cp> f(k+1);
	for(int i=0;i<=n;i++)f[i].x=a[i];
	for(int i=1;i<=k-1;i++)//二进制翻转
		rev[i]=(rev[i>>1]>>1)|((i&1)<<(ci-1));
	FFT(k,f,1,rev);
	for(int i=0;i<=k;i++)
		f[i]=f[i]*f[i];
	FFT(k,f,-1,rev);
	for(int i=0;i<=n;i++)
        a[i]=((int)(f[i].x/k+0.5))&1;
}
vector<int> ksm(vector<int> a,ll p){
    vector<int>ans={1};
    int sz=a.size();
    while(p){
        if(p&1){
            solve(ans,a,ans);
            ans.resize(sz);
        }
        m2(a);
        p>>=1;
    }
    return ans;
}
int main(){
    int n;
    ll k;
    scanf("%d%lld",&n,&k);
    n--;
    int ci=1;
    while(ci<n+1)ci<<=1;
    k%=ci;
    vector<int> a(n+1),ans(n+1);
    for(int i=0;i<=n;i++){
        scanf("%d",&a[i]);
    }
    vector<int> c(n+1);
    for(int i=0;i<=n;i++)c[i]=1;
    c=ksm(c,k);
    for(int p=1;p<=17;p++){
        vector<int> b(n+1);
        for(int i=0;i<=n;i++){
            b[i]=a[i]&1;
            a[i]>>=1;
        }
        vector<int> res;
        solve(b,c,res);
        for(auto &x:res)x&=1;
        for(int i=0;i<=n;i++){
            if(res[i])
                ans[i]|=(1<<(p-1));
        }
    }
    for(int i=0;i<=n;i++){
        if(i>0)printf(" ");
        printf("%d",ans[i]);
    }
    return 0;
}

标签:Orz,Cup,int,FFT,db,vector,ans,return,cp
来源: https://www.cnblogs.com/ucprer/p/14396462.html