题目大意:给定一个长为$n$($nleq 10^6$)的序列S,定义一个合法的五元组$(a,b,c,d,e)$合法当且仅当
$$ ( S_a mid S_b ) and S_c and ( S_d igotimes S_e ) = 2^k $$
$$ S_a and S_b = 0 $$
对于所有的合法的五元组,求 $ sum_{合法(a,b,c,d,e)} F( S_a mid S_b ) imes F( S_c ) imes F( S_d igotimes S_e ) $
其中$F(i)$表示斐波那契数列第i项 $F(1)=0,F(2)=1$
$S_ileq 2^{17}$ ,答案对1e9+7取模。
先放一篇大佬的博客,我理解这道题的方式以及本文的思维方法都对该文略有参考。
https://blog.csdn.net/BeNoble_/article/details/79512236
看到这道题,看到应该想到对于一个五元组$(a,b,c,d,e)$,abcde具体的值并没有意义,真正有意义的是S中的值,所以我们不记录S[],而是记录t[]表示每个数的数量。
然后,我们显然可以把式子分成三个部分:
左侧 $j and k==0igcap jmid k==i$ 意义下多项式的卷积
中间 多项式
右侧 $sum_{[jigotimes k==i]}$ 意义下的卷积
合并之前,我们可以直接对每个部分的多项式的每个位置乘上对应的斐波那契序列值,然后合并,最后统计答案。
步骤:
1、求左侧部分的卷积
2、对中间部分赋值
3、求右侧部分的卷积
4、合并三个部分,枚举每一个2的k次方项累计答案
其中,步骤2最为简易,直接枚举数组位置一个接一个赋值就行,步骤3,4都可以用FWT即快速沃尔什(逆)变换解决,而第一步就需要一点智慧了(详见2015vfk集训队论文)。
我们将 j|k==i&&j&k==0 的条件转化为 $j|k==i$&&$|j|+|k|==|i|$,其中$|x|$表示x二进制下1的个数。于是,我们对于每一个$|x|$记录一个数组p,把原来的数$t[x]$放到$p[|x|][x]$中,然后对于每一层$|x|$进行$j|k==i$意义下的卷积。然后令$g[|x|][i]=sum_{j+k=|x|} p[j][i] + p[k][i]$,再将每一层|x|进行一次逆变换,最后,对于每一个pos,将$g[|pos|][pos]$提出来乘以相应位置的斐波那契序列值形成一个新的数组,里面存的就是我们要的第一步的卷积了。
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #define LL long long #define mid (l+r>>1) #define mod 1000000007ll #define inv2 (mod+1>>1) #define M 525000ll #define OR 1ll #define AND 2ll #define XOR 3ll using namespace std; LL read(){ LL nm=0,fh=1;char cw=getchar(); for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh; for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0'); return nm*fh; } void write(LL x){ if(x<0){putchar('-');return write(-x);} if(x>9) write(x/10); putchar(x%10+'0'); } LL n,m,A[M],B[M],C[M],len,ans[M],bt[M],fib[M],maxn,top; LL p[18][M],R[18][M],tt,t[M]; LL tk[M],h[M],w[M]; LL add(LL x,LL y){return (x+y+mod+mod)%mod;} LL mul(LL x,LL y){return ((x*y%mod)+mod)%mod;} void FWT(LL *x,LL tpe,LL kd){ for(LL tot=1;tot<len;tot<<=1){ for(LL st=0;st<len;st+=(tot<<1)){ for(LL pos=st;pos<st+tot;pos++){ LL t1=x[pos],t2=x[pos+tot]; if(tpe==OR) x[pos]=t1,x[pos+tot]=add(t1*kd,t2); if(tpe==AND) x[pos]=add(t1,kd*t2),x[pos+tot]=t2; if(tpe==XOR){ if(kd==1) x[pos]=add(t1,t2),x[pos+tot]=add(t1,-t2); else x[pos]=mul(add(t1,t2),inv2),x[pos+tot]=mul(add(t1,-t2),inv2); } } } } } int main(){ //freopen(".in","r",stdin); //freopen(".out","w",stdout); n=read(),fib[0]=0,fib[1]=1; for(LL i=2;i<M;i++) fib[i]=add(fib[i-1],fib[i-2]); //calculate fibonacci for(LL i=1;i<M;i++) bt[i]=bt[i-(i&-i)]+1; //calculate Bit_count for(LL i=1;i<=n;i++) m=read(),t[m]++,maxn=max(maxn,m); //Trun --> a1,a2,a3 into --> sumof(1) sumof(2)... at t[] for(len=1;len<=maxn;len<<=1) top++; for(LL i=0;i<len;i++) h[i]=t[i],p[bt[i]][i]=t[i]; //create new array of t[] initialize p(i,j) for(LL i=0;i<=top;i++) FWT(p[i],OR,1ll); for(LL i=0;i<=top;i++){ for(LL j=0;j+i<=top;j++){ for(LL pos=0;pos<len;pos++) R[i+j][pos]=add(R[i+j][pos],mul(p[i][pos],p[j][pos])); } } for(LL i=0;i<=top;i++) FWT(R[i],OR,-1ll); //GET p(i,j): Sigma Bitcount=i for(LL i=0;i<len;i++) B[i]=mul(t[i],fib[i]); //GET Bi : fib[i]*t[i] FWT(h,XOR,1ll); for(LL i=0;i<len;i++) C[i]=mul(h[i],h[i]); FWT(C,XOR,-1ll); for(LL i=0;i<len;i++) C[i]=mul(C[i],fib[i]); //GET: Ci : fib[i]* ( Sgm j^k==i t[j]*t[k] ) for(LL i=0;i<len;i++) A[i]=mul(fib[i],R[bt[i]][i]); //GET Ai : fib[i]* Sgm j|k==i&&|j|+|k|==|i| t[j]*t[k] FWT(A,AND,1ll),FWT(B,AND,1ll),FWT(C,AND,1ll); //FWT to A,B,C for(LL i=0ll;i<len;i++) ans[i]=mul(A[i],mul(B[i],C[i])); FWT(ans,AND,-1ll); //FWT to A*B*C --> ans --> UFWT ans for(LL i=1ll;i<len;i<<=1) tt=add(tt,ans[i]); write(tt); //print answer return 0; }