题意
有2^n个国家(0~2^n-1编号),每个国家最初有a[i]个货物。每过一天,假如编号i与编号j异或之后二进制1的个数为1,则国家i会增加上一天国家j的货物,国家j类似。求t天之后各个国家的货物数目。
题解
对于第t天交易,我们可以得到以下式子:
我们可以将公式变换一下
这样我们就可以用FWT对原数组快速修改了。定义num数组是更新数组,我们将num[0],num[1],num[2]..num[2^k]全部标记为1,对于过去一天,我们可以得到答案数组 那么做第二天的时候就可以得到 ,所以做t天的时候答案是 。
AC代码
#include<stdio.h>
#include<string.h>
#define mod 1000000007
typedef long long ll;
ll rev=mod+1>>1;
void FWT(ll a[],ll n)
{
for(ll d=1;d<n;d<<=1)
for(ll m=d<<1,i=0;i<n;i+=m)
for(ll j=0;j<d;j++)
{
ll x=a[i+j],y=a[i+j+d];
a[i+j]=(x+y)%mod,a[i+j+d]=(x-y+mod)%mod;
//xor:a[i+j]=x+y,a[i+j+d]=(x-y+mod)%mod;
//and:a[i+j]=x+y;
//or:a[i+j+d]=x+y;
}
}
void UFWT(ll a[],ll n)
{
for(ll d=1;d<n;d<<=1)
for(ll m=d<<1,i=0;i<n;i+=m)
for(ll j=0;j<d;j++)
{
ll x=a[i+j],y=a[i+j+d];
a[i+j]=1LL*(x+y)*rev%mod,a[i+j+d]=(1LL*(x-y)*rev%mod+mod)%mod;
//xor:a[i+j]=(x+y)/2,a[i+j+d]=(x-y)/2;
//and:a[i+j]=x-y;
//or:a[i+j+d]=y-x;
}
}
ll qmi(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b%2)ans=ans*a%mod;
a=a*a%mod;
b/=2;
}
return ans;
}
void solve(ll a[],ll b[],ll n,ll m)
{
FWT(a,n);
FWT(b,n);
for(ll i=0;i<n;i++) a[i]=a[i]*qmi(b[i],m)%mod;
UFWT(a,n);
}
ll a[1<<20|5],num[1<<20|5];
int main()
{
ll n,m;
scanf("%lld%lld",&n,&m);
for(ll i=0;i<(1<<n);i++)
scanf("%lld",&a[i]);
num[0]=1;
for(ll i=0;i<n;i++)
num[1<<i]=1;
solve(a,num,(1<<n),m);
for(ll i=0;i<(1<<n);i++)printf("%lld ",a[i]);
printf("\n");
}