ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-02-10 20:03:03  阅读:224  来源: 互联网

标签:Orz Cup int FFT db vector ans return cp


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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有