题解 P6673 【[清华集训2016] 石家庄的工人阶级队伍比较坚强】
其实是挺无聊的一个板子题,我不会做真的是败笔。
构造数列
显然我们对它进行三进制的 FWT,即高维 FFT。FFT 的意义是多项式在单位根处的取值,但是我们不方便求三次单位根。
作为一个熟练的选手立即想到扩域。将每个复数表示为
IFWT 的式子可以手解一下三元一次方程。但是我们还需要证明
FWT 是线性变换,所以直接 FWT 过去之后快速幂一下再 IFWT 回来即可。
时间复杂度
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll readint(){
ll x=0;
bool f=0;
char c=getchar();
while(!isdigit(c)&&c!='-') c=getchar();
if(c=='-'){
f=1;
c=getchar();
}
while(isdigit(c)){
x=x*10+c-'0';
c=getchar();
}
return f?-x:x;
}
const int maxn=5.4e5,maxm=12+5;
int m,n=1,t,p,b[maxm][maxm],phip;
struct cp{
int a,b;
cp operator +(cp x){
return {(a+x.a)%p,(b+x.b)%p};
}
cp operator *(cp x){
return {(int)((1ll*a*x.b%p+1ll*b*x.a%p-1ll*a*x.a%p+p)%p),(int)((1ll*b*x.b%p-1ll*a*x.a%p+p)%p)};
}
cp operator *(ll x){
return {(int)(1ll*a*x%p),(int)(1ll*b*x%p)};
}
}f[maxn],g[maxn];
cp ksm(cp a,int b){
cp ans={0,1};
while(b){
if(b%2==1) ans=ans*a;
a=a*a;
b/=2;
}
return ans;
}
int phi(int n){
int res=n;
for(int i=2;i*i<=n;i++) if(n%i==0){
res=1ll*res*(i-1)/i;
while(n%i==0) n/=i;
}
if(n>1) res=1ll*res*(n-1)/n;
return res;
}
int ksm(int a,int b){
int ans=1;
while(b){
if(b%2==1) ans=1ll*ans*a%p;
a=1ll*a*a%p;
b/=2;
}
return ans;
}
void FWT(cp* f,int n,bool flag){
for(int i=1;i<n;i*=3) for(int j=0;j<n;j+=i*3) for(int k=j;k<j+i;k++){
cp t0=f[k],t1=f[k+i],t2=f[k+i*2];
if(flag){
f[k]=t0+t1+t2;
f[k+i]=t0+t1*(cp){1,0}+t2*(cp){p-1,p-1};
f[k+i*2]=t0+t1*(cp){p-1,p-1}+t2*(cp){1,0};
}
else{
f[k]=t0+t1+t2;
f[k+i]=t0+t1*(cp){p-1,p-1}+t2*(cp){1,0};
f[k+i*2]=t0+t1*(cp){1,0}+t2*(cp){p-1,p-1};
}
}
if(!flag){
int invn=ksm(n,phip-1);
for(int i=0;i<n;i++) f[i]=f[i]*invn;
}
}
int main(){
#ifdef LOCAL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
m=readint();
for(int i=0;i<m;i++) n*=3;
t=readint();
p=readint();
for(int i=0;i<n;i++) f[i].b=readint();
for(int i=0;i<=m;i++) for(int j=0;j<=m-i;j++) b[i][j]=readint();
for(int i=0;i<n;i++){
int x=i,cnt1=0,cnt2=0;
for(int j=0;j<m;j++){
if(x%3==1) cnt1++;
else if(x%3==2) cnt2++;
x/=3;
}
g[i].b=b[cnt1][cnt2];
}
phip=phi(p);
FWT(f,n,1);
FWT(g,n,1);
for(int i=0;i<n;i++) f[i]=f[i]*ksm(g[i],t);
FWT(f,n,0);
for(int i=0;i<n;i++) printf("%d\n",f[i].b);
#ifdef LOCAL
fprintf(stderr,"%f\n",1.0*clock()/CLOCKS_PER_SEC);
#endif
return 0;
}