1. 程式人生 > >【CF914G】Sum the Fibonacci 快速??變換模板

【CF914G】Sum the Fibonacci 快速??變換模板

斐波那契數列 合並 gpo get ostream printf cpp ros pac

【CF914G】Sum the Fibonacci

題解:給你一個長度為n的數組s。定義五元組(a,b,c,d,e)是合法的當且僅當:

1. $1\le a,b,c,d,e\le n$
2. $(s_a|s_b) \& s_c \& (s_d $^$ s_e)=2^i$,i是某個整數
3. $s_a \& s_b=0$

求$\sum f(s_a|s_b) * f(s_c) * f(s_d $^$ s_e)$,f是斐波那契數列,對於所有合法的五元組(a,b,c,d,e)。答案模$10^9+7$。

$1\le n\le 10^6,0\le s_i< 2^17$

題解:說白了就是求:子集和卷積,異或卷積,與卷積。後面兩個好求,學了一發子集和卷積。說白了就是強行加了一個占位多項式,即將數組多開一維記錄子集中1的個數。然後合並時相當於背包合並,時間復雜度$O(n^22^n)$。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

typedef long long ll;
const int maxn=(1<<17)+4;
const ll P=1000000007;
const ll inv=500000004;
ll a[maxn],c[maxn],d[maxn];
ll fa[maxn][18],fb[maxn][18],f[maxn];
ll ans;
int cnt[maxn];
int n,len;
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
int main()
{
	n=rd();
	int h,i,j,k,x,y,v;
	f[1]=cnt[1]=1;
	len=1<<17;
	for(i=2;i<len;i++)	f[i]=(f[i-1]+f[i-2])%P,cnt[i]=cnt[i-(i&-i)]+1;
	for(i=1;i<=n;i++)	v=rd(),fa[v][cnt[v]]++,d[v]++,c[v]=(c[v]+f[v])%P;
	for(h=1;h<len;h<<=1)	for(i=0;i<len;i++)	if(i&h)	for(j=0;j<=17;j++)
		fa[i][j]=(fa[i][j]+fa[i-h][j])%P;
	for(i=0;i<len;i++)	for(j=0;j<=17;j++)	for(k=0;k<=j;k++)
		fb[i][j]=(fb[i][j]+fa[i][k]*fa[i][j-k])%P;
	for(h=1;h<len;h<<=1)	for(i=0;i<len;i++)	if(i&h)	for(j=0;j<=17;j++)
		fb[i][j]=(fb[i][j]-fb[i-h][j]+P)%P;
	for(i=0;i<len;i++)	a[i]=(a[i]+f[i]*fb[i][cnt[i]])%P;
	for(h=1;h<len;h<<=1)	for(i=0;i<len;i++)	if(i&h)
		x=d[i-h],y=d[i],d[i-h]=(x+y)%P,d[i]=(x-y+P)%P;
	for(i=0;i<len;i++)	d[i]=d[i]*d[i]%P;
	for(h=1;h<len;h<<=1)	for(i=0;i<len;i++)	if(i&h)
		x=d[i-h],y=d[i],d[i-h]=(x+y)*inv%P,d[i]=(x-y+P)*inv%P;
	for(i=0;i<len;i++)	d[i]=d[i]*f[i]%P;
	for(h=1;h<len;h<<=1)	for(i=0;i<len;i++)	if(!(i&h))
		a[i]=(a[i]+a[i+h])%P,c[i]=(c[i]+c[i+h])%P,d[i]=(d[i]+d[i+h])%P;
	for(i=0;i<len;i++)	a[i]=a[i]*c[i]%P*d[i]%P;
	for(h=1;h<len;h<<=1)	for(i=0;i<len;i++)	if(!(i&h))
		a[i]=(a[i]-a[i+h]+P)%P;
	for(i=0;i<17;i++)	ans=(ans+a[1<<i])%P;
	printf("%lld",ans);
	return 0;
}

【CF914G】Sum the Fibonacci 快速??變換模板