Description

【HNOI2016模拟4.4】Fenwit_GDOI模拟

Solution

虽然这题还有很多的方法,但是这类型的题目斗游戏一个通用的做法FWT(快速沃尔什变换),而且非常的短。
首先我们可以把式子转化成:

Fi+1[jk]=∑k=02M−1Fi[k]∗B[Cnt(j)]


根据异或的性质,这个很显然。


然后就用后面的两个数组做FWT,就可以了。


但是还要注意一个问题,就是这个模数可能的2没有逆元,FWT的时候是要除以2的。


我们探究一下,要使


A2m≡B(modC)(FWT有m层)因为有mod,B< C


根据模的性质,这个有


A≡B∗2m(modC∗2m)

此时A就不用除了,然后给C乘上

2m次方,最后再用B除上 2m

还有一个优化,我们可以发现T十分的大,但是还要快速幂。


其实我们可以发现扩展欧拉定理在矩阵乘法上也适用,所以我们可以先把T的phi求出来,然后再搞一搞。

Code

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=3e5+7;
typedef long long ll;
ll i,j,k,l,t,n,m,ans,mo,T,ni2,pp,ui;
ll a[maxn],b[maxn],er[20],c[maxn];
bool bz;
char ch;
ll phi(ll x){
ll i,j=x;
for(i=2;i<=x;i++){
if(x%i==0)j=j*(i-1)/i;
while(x%i==0)x/=i;
}
if(x!=1)j=j*(x-1)/x;
return j;
}
ll che(ll x,ll y){
ll z=(ll)((double)x*y/mo+1e-5)*mo;
return x*y-z;
}
ll qsm(ll x,ll y){
ll z=1;
for(;y;y/=2,x=che(x,x))if(y&1)z=che(x,z);
return z;
}
void fwt(ll *a,ll n){
ll i,j,k,x,y;
for(k=1;k<n;k*=2){
for(i=0;i<n;i+=k*2){
fo(j,0,k-1){
x=a[i+j],y=a[i+j+k];
a[i+j]=(x+y)%mo,a[i+j+k]=(x-y+mo)%mo;
}
}
}
}
void ufwt(ll *a,ll n){
ll i,j,k,x,y;
for(k=1;k<n;k*=2){
for(i=0;i<n;i+=k*2){
fo(j,0,k-1){
x=a[i+j],y=a[i+j+k];
a[i+j]=(x+y)%mo,a[i+j+k]=(x-y+mo)%mo;
}
}
}
}
void gao(ll *a,ll *b,ll n){
ll i;
fwt(a,n),fwt(b,n);
fo(i,0,n)
a[i]=che(a[i],qsm(b[i],ui+pp*(bz)));
ufwt(a,n);
}
int main(){
// freopen("fan.in","r",stdin);
// freopen("fan.out","w",stdout);
er[0]=1;fo(i,1,19)er[i]=er[i-1]*2;
scanf("%lld%lld",&m,&mo);pp=phi(mo);
ch=getchar();while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9'){
ui=ui*10+ch-'0';if(ui>=pp)bz=1;ui%=pp;
ch=getchar();
}
fo(i,0,er[m]-1)scanf("%lld",&a[i]);
fo(i,0,m+1)scanf("%lld",&b[i]);
fo(i,0,er[m]-1){
fo(j,1,m)if(i&er[j-1])c[i]++;
c[i]=b[c[i]];
}
mo*=er[m];
gao(a,c,er[m]-1);
fo(i,0,er[m]-1)printf("%lld\n",a[i]/er[m]);
}