CF914G-题解

前置芝士:FWTFMT子集卷积


题意已经说得相当清楚了,这里就不复述了。


正片

想到这个 \(\sum f(s_a|s_b)*f(s_c)*f(s_d \wedge s_e)\) 并不是很好搞,考虑转换,去掉 \(\sum\)

开个桶 \(\displaystyle cnt[i]=\sum_{i=0}^n [s_j=i]\),那么原式就变成了:

\[cnt[s_a|s_b]*f(s_a|s_b)*cnt[s_c]*f(s_c)*cnt[s_d \wedge s_e]*f(s_d \wedge s_e) \]

显然,剩下的就是用上前置芝士的部分了。

考虑 \(cnt[s_a|s_b]\) 部分的卷积,在卷这一部分的时候还有一个限制是 \(s_a\&s_b=0\),这样就不能直接上 FMT 了,需要跑子集卷积,自己卷自己。复杂度 \(O(nlog^2n)\)

具体的卷法可以看代码,也可以去看模板题,反正这道题里的子集卷积还是挺板的。

再考虑 \(cnt[s_c]\),没得卷。复杂度 \(O(n)\)

再考虑 \(cnt[s_d \wedge s_e]\) 部分的卷积,很显然直接跑 FWT 就行了,脑子都不用的。复杂度 \(O(nlogn)\)

卷完之后每个位置乘上相应的斐波那契数就行了。

最后把这些东西卷到一起去。题目中的限制是 &,那么最后跑一遍 FMT 把三个部分卷在一起。

由于还要求了是 \(2^i\) 的项,那么输出所有 \(2^i\) 项的和就好了。

总复杂度是 \(O(nlog^2n)\) 级别的。

写在最后但是在代码前

FWT 和 FMT 可以直接作为函数封装起来不去管他。其实子集卷积也可,就是我有点懒 qwq。

如果你认为自己的代码应该八九不离十了,但就是死活过不去,请检查以下三点:

  • 输入、输出有没有错;
  • 执行卷积的数组打没打错;
  • 写好的预处理(等)函数加没加进主函数。

如果查不出来,那就重构吧。不过对于封装好的函数,尽量相信自己。

Code

放一份方便调试的代码。 owo

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define mo 1000000007ll
#define m2 (mo+1)/2
#define sc(i) scanf("%lld",&i);

inline void fmta(int *a,int n,int x) {for(int s=2,t=1;s<=n;s<<=1,t<<=1) for(int i=0;i<n;i+=s) for(int j=0;j<t;++j) a[i+j]=(a[i+j+t]*x+a[i+j]+mo)%mo;}
inline void fmto(int *a,int n,int x) {for(int s=2,t=1;s<=n;s<<=1,t<<=1) for(int i=0;i<n;i+=s) for(int j=0;j<t;++j) a[i+j+t]=(a[i+j+t]+a[i+j]*x+mo)%mo;}
inline void fwt(int *a,int n,int x) {for(int s=2,t=1;s<=n;s<<=1,t<<=1) for(int i=0;i<n;i+=s) for(int j=0;j<t;++j) {a[i+j]=(a[i+j+t]+a[i+j])%mo; a[i+j+t]=(a[i+j]-a[i+j+t]-a[i+j+t]+mo*2)%mo*x%mo; a[i+j]=a[i+j]*x%mo;}}
#define ctt(i) __builtin_popcount(i)

#define N 1<<18|1
int a[20][N],b[20][N],c[N],d[N],s[N];
int cnt[N],f[N],n;
inline void gf() {f[0]=0; f[1]=1; f[2]=1; for(int i=3;i<(1<<17);++i) f[i]=(f[i-1]+f[i-2])%mo;}

signed main()
{
    sc(n);
    gf();
    int mx=-1;
    for(int i=1;i<=n;++i) {int x; sc(x); ++cnt[x]; mx=max(mx,x);}

    int ctn=0,lcn=1;
    while(lcn<=mx) {ctn++; lcn<<=1;}
    

    for(int i=0;i<=mx;++i) a[ctt(i)][i]=c[i]=cnt[i];
    for(int i=0;i<=ctn;++i) fmto(a[i],lcn,1);
    for(int i=0;i<=ctn;++i) for(int j=0;j<=i;++j) for(int k=0;k<lcn;++k) b[i][k]=(b[i][k]+a[j][k]*a[i-j][k]%mo)%mo;
    for(int i=0;i<=ctn;++i) fmto(b[i],lcn,-1);
    

    for(int i=0;i<lcn;++i) d[i]=b[ctt(i)][i]*f[i]%mo;


    fwt(c,lcn,1);
    for(int i=0;i<lcn;++i) c[i]=c[i]*c[i]%mo;
    fwt(c,lcn,m2);
    for(int i=0;i<lcn;++i) c[i]=c[i]*f[i]%mo;
    
    for(int i=0;i<lcn;++i) s[i]=cnt[i]*f[i]%mo;

    
    fmta(s,lcn,1);
    fmta(d,lcn,1);
    fmta(c,lcn,1);
    for(int i=0;i<lcn;++i) s[i]=s[i]*d[i]%mo*c[i]%mo;
    fmta(s,lcn,-1);


    int ans=0;
    for(int i=1;i<=lcn;i<<=1) ans=(ans+s[i])%mo;
    printf("%lld",ans);
    return 0;
}
posted @ 2023-02-09 17:05  Fy5枫叶  阅读(85)  评论(0)    收藏  举报