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;
}

猜你喜欢

转载自www.cnblogs.com/Narh/p/10259830.html