分析:要从
e
i
[
u
]
=
∑
e
(
i
−
1
)
[
u
]
∗
b
[
f
(
u
,
v
)
]
ei[u] = \sum e(i-1)[u] * b[f(u,v)]
ei[u]=∑e(i−1)[u]∗b[f(u,v)]入手,将式子写得简单点,也就是:
f
[
i
]
=
∑
f
[
j
]
∗
b
[
c
[
i
⨁
j
]
]
f[i] = \sum f[j] * b[c[i\bigoplus j]]
f[i]=∑f[j]∗b[c[i⨁j]],令
d
[
i
]
d[i]
d[i] = b[c[i]]。
则有
f
[
i
]
=
∑
f
[
j
]
∗
d
[
i
⨁
j
]
f[i] = \sum f[j] * d[i\bigoplus j]
f[i]=∑f[j]∗d[i⨁j],很明显是一个卷积,这意味着可以在
n
∗
l
o
g
(
n
)
n*log(n)
n∗log(n)的时间内快速计算出所有的
f
[
i
]
f[i]
f[i],即
e
i
ei
ei。
由于要卷t次,还要套一个快速幂,总体复杂度为
O
(
n
∗
l
o
g
(
n
)
∗
l
o
g
(
t
)
)
O(n*log(n)*log(t))
O(n∗log(n)∗log(t))
这题特殊的地方在于模数不是质数,那么FWT在逆变换时可能得不到逆元,因此要用另外一种处理:将模数乘上长度,逆变换时就是在正变换的基础上每一项除以长度,由于模数变得很大,在快速幂和点值相乘时就需要用到黑科技o(1)快速乘。
(虽然不知道为什么可以这样搞,但是好像逆变化都可以这样搞,而且逆变化的样式和原来不同了,先记着吧,待补)
(用dp的方法得到 C 数组也有点妙啊)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e6 + 10;
ll m,t,p;
ll mul(ll x,ll y,ll MOD) {
ll res=x*y-(ll)((long double)x/MOD*y+0.1)*MOD;
return (res%MOD+MOD)%MOD;
}
ll a[maxn],b[maxn],c[maxn],d[maxn];
void fwt(ll a[],int len,int f) {
for(int s = 2; s <= len; s <<= 1) {
for(int j = 0; j < len; j += s) {
for(int k = 0; k < s / 2; k++) {
ll x = a[j + k],y = a[j + k + s / 2];
a[j + k] = (x + y) % p;
a[j + k + s / 2] = (x - y + p) % p;
}
}
}
if(f == -1)
for(int i = 0; i < len; i++)
a[i] /= len;
}
ll fpow(ll a,ll b,ll mod) {
ll r = 1;
while(b) {
if(b & 1) r = mul(r,a,mod);
a = mul(a,a,mod);
b >>= 1;
}
return r % mod;
}
int main() {
scanf("%lld%lld%lld",&m,&t,&p);
ll n = (1ll << m);
for(int i = 0; i < n; i++)
scanf("%lld",&a[i]);
for(int j = 0; j <= m; j++)
scanf("%lld",&b[j]);
for(int j = 0; j < n; j++)
c[j] = c[j >> 1] + (j & 1);
for(int i = 0; i < n; i++)
d[i] = b[c[i]];
p *= n;
fwt(a,n,1);fwt(d,n,1);
for(int i = 0; i < n; i++) {
a[i] = mul(a[i],fpow(d[i],t,p),p);
}
fwt(a,n,-1);
for(int i = 0; i < n; i++)
printf("%lld\n",a[i]);
return 0;
}