CF 914G Sum the Fibonacci——子集卷积
题目:http://codeforces.com/contest/914/problem/G
第一个括号可以子集卷积;第三个括号可以用 FWT 异或卷积;这样算出选两个数组成 x 的方案数;三个部分的方案数分别乘上 f[ x ] 再一起与卷积即可。
注意子集卷积的时候不要改 tp[ i ][ s ] ,因为要的是恰好两个数拼起来,没有改过的(但是做过 FMT 的) tp[ i ][ s ] 存的是初值,表示选 1 个数的方案数。
所以如果可以选任意多个数,就可以像背包一样, tp[ j ][ s ] 用的改过的, tp[ i-j ][ s ] 用没改过的。
累计完 tp[ i ][ s ] 的时候,要在 i 这一层 iFMT 回去,再贡献给 a[ s ] ,不要直接加到 a[ s ] 上、做完所有的 i 之后再 iFMT 回去,因为 iFMT 只能弄回去对于同一个 i 的。
卷积的时候不要对 i - j == j 的情况去重,因为可以选重复的。
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret; } int Mx(int a,int b){return a>b?a:b;} const int K=17,N=(1<<K)+5,mod=1e9+7; int f[N],a[N],b[N],c[N],bin[K+5],len,bh; int ct[N],tp[K+5][N]; void upd(int &x){x>=mod?x-=mod:0;} void init() { bin[0]=1;for(int i=1;i<=K;i++)bin[i]=bin[i-1]<<1; for(int s=1,j=bin[K];s<j;s++)ct[s]=ct[s-(s&-s)]+1; f[0]=0;f[1]=1;for(int i=2,j=bin[K];i<j;i++)f[i]=f[i-1]+f[i-2],upd(f[i]); } void fwt_and(int *a,bool fx) { for(int R=2;R<=len;R<<=1) for(int i=0,m=R>>1;i<len;i+=R) for(int j=0;j<m;j++) a[i+j]+=(fx?mod-a[i+m+j]:a[i+m+j]),upd(a[i+j]); } void dv2(int &x){if(x&1)x=(x+mod)>>1; else x>>=1;} void fwt_xor(int *a,bool fx) { for(int R=2;R<=len;R<<=1) for(int i=0,m=R>>1;i<len;i+=R) for(int j=0;j<m;j++) { int x=a[i+j],y=a[i+m+j]; a[i+j]=x+y; a[i+m+j]=x+mod-y; upd(a[i+j]); upd(a[i+m+j]); if(fx)dv2(a[i+j]),dv2(a[i+m+j]); } } void fmt(int *a,bool fx) { for(int i=1;i<len;i<<=1) for(int s=0;s<len;s++) if(s&i)a[s]+=(fx?mod-a[s^i]:a[s^i]),upd(a[s]); } void cz() { int t[N]; for(int i=0;i<=bh;i++)fmt(tp[i],0);//<= not < for(int i=0;i<=bh;i++) { for(int s=0;s<len;s++)t[s]=0; for(int j=0;j<=i;j++) for(int s=0;s<len;s++) t[s]=(t[s]+(ll)tp[j][s]*tp[i-j][s])%mod; fmt(t,1); for(int s=0;s<len;s++) if(i==ct[s])a[s]+=t[s],upd(a[s]); } /* for(int i=0;i<=bh;i++) { for(int j=0;j<=i;j++) for(int s=0;s<len;s++) { if(i!=ct[s])continue;// a[s]=(a[s]+(ll)tp[j][s]*tp[i-j][s])%mod;//i-j==j is ok! } } fmt(a,1); */ } int main() { init();int n=rdn(),mx=0; for(int i=1,d;i<=n;i++) { d=rdn();mx=Mx(mx,d); tp[ct[d]][d]++;c[d]++;b[d]+=f[d];upd(b[d]); } for(bh=1;bin[bh]<=mx;bh++);len=bin[bh]; cz(); fwt_xor(c,0);for(int i=0;i<len;i++)c[i]=(ll)c[i]*c[i]%mod;fwt_xor(c,1); for(int i=0;i<len;i++)a[i]=(ll)a[i]*f[i]%mod; for(int i=0;i<len;i++)c[i]=(ll)c[i]*f[i]%mod; fwt_and(a,0); fwt_and(b,0); fwt_and(c,0); for(int i=0;i<len;i++)a[i]=(ll)a[i]*b[i]%mod*c[i]%mod; fwt_and(a,1); int ans=0; for(int i=1;i<len;i<<=1)ans+=a[i],upd(ans); printf("%d\n",ans); return 0; }