【51Nod1773】A国的贸易 解题报告
【51Nod1773】A国的贸易
Description
给出一个长度为 \(2^n\) 的序列,编号从\(0\)开始。每次操作后,如果 \(i\) 与 \(j\) 的二进制表示只差一位则第 \(i\) 个数会加上操作前的第 \(j\) 个数。求 \(t\) 次操作后序列中的每个数是多少。
Input
第一行两个正整数 \(n\) ,\(t\),意义如题。
第二行 \(2^n\) 个非负整数,第 \(i\) 个数表示编号为 \(i-1\) 的城市的初始货物存储量。
\(n<=20 \ t<=10^9\)
Output
输出一行 \(2^n\) 个非负整数。
第 \(i\) 个数表示过了 \(t\) 天后,编号为 \(i-1\) 的城市上的货物数量对 \(1e9+7\) 取模的结果。
构造一个多项式\(F\),\(F_i=1\),当\(i\)是\(2\)的正整数幂的时候。
然后答案就是\(D*F^t\),乘法是异或子集和,用\(FWT\)就可以了。
注意到不要写多项式快速幂,子集运算的长度是封闭的,所以化成类似点乘的形式后做快速幂即可,这样复杂度是\(O(n2^n)\)的
Code:
#include <cstdio>
const int N=(1<<20)+10;
const int mod=1e9+7,inv=500000004;
namespace Fast{
const int LEN=10000000;
char inp[LEN],outp[LEN];
int tmp[20];
int inpos,outpos;
void init(){
fread(inp,1,LEN,stdin);
inpos=0; outpos=0;
}
char GetChar(){return inp[inpos++];}
int read(){
int ret=0; char ch=GetChar();
while (ch<'0'||ch>'9') ch=GetChar();
while ('0'<=ch&&ch<='9') ret=ret*10+ch-'0',ch=GetChar();
return ret;
}
void PutChar(char ch){outp[outpos++]=ch;}
void print(int x){
int pos=0;
if (!x) tmp[++pos]=0;
else
while (x) tmp[++pos]=x%10,x/=10;
for (int i=pos;i>=1;--i) PutChar(tmp[i]+'0');
}
void Print(){fwrite(outp,1,outpos,stdout);}
}
#define mul(a,b) (1ll*(a)*(b)%mod)
#define add(a,b) ((a+b)%mod)
int n,len,t,T[N],D[N];
void xorfwt(int *a,int typ)
{
for(int le=1;le<len;le<<=1)
for(int p=0;p<len;p+=le<<1)
for(int i=p;i<p+le;i++)
{
int tx=a[i],ty=a[i+le];
a[i]=add(tx,ty),a[i+le]=add(tx,mod-ty);
if(!typ) a[i]=mul(a[i],inv),a[i+le]=mul(a[i+le],inv);
}
}
int qp(int d,int k){int f=1;while(k){if(k&1)f=mul(f,d);d=mul(d,d),k>>=1;}return f;}
int main()
{
Fast::init();
n=Fast::read(),t=Fast::read(),len=1<<n;
for(int i=0;i<len;i++) T[i]=Fast::read();
D[0]=1;for(int i=0;i<n;i++) D[1<<i]=1;
xorfwt(T,1),xorfwt(D,1);
for(int i=0;i<len;i++) T[i]=mul(T[i],qp(D[i],t));
xorfwt(T,0);
for(int i=0;i<len;i++) Fast::print(T[i]),Fast::PutChar(' ');
Fast::Print();
return 0;
}
2018.12.18