Fenwit FWT+数论

来源:互联网 发布:淘宝零食视频 编辑:程序博客网 时间:2024/06/10 06:03

题意

这里写图片描述
这里写图片描述

分析

这题好神啊!!!

这题大概题意就是说给出一个数组f,设c[i,j]=b[cnt(i^j)],那么答案就是fcT
但这么做显然只有20分,而这20分是只要暴力就可以拿到的。

我们可以重新定义c[i]=b[cnt(i)],那么显然有fi+1[i]=fi[j]c[k](jk==i),我们把这种运算定义为运算。那么答案就是f0cT。由于运算是满足结合律的,于是我们可以先算出cT
那如何快速计算呢?FWT!!!
FWT就是专门解决快速类似进行运算之类的问题,可以把异或换成与,或之类的二进制运算。
FWT写起来与fft类似,比fft更简洁一点,我觉得介绍FWT比较好的博文是这篇和这篇。至于要自己推一遍的话,这个坑就留着改天再补好了。

有了FWT这个神器之后,再回到这题。现在问题在于如何计算cT。由于FWT的性质,我们只用对c做一次FWT变换,然后把变换后的c点乘T次就好了。这里由于T比较大,可以用到欧拉定理EXT:axaxmodφ(p)+φ(p)(modp)(必须满足x>=φ(p))>
又注意到在做逆FWT变换的时候,由于有/2运算,而由于模数不确定,所以不一定存在逆元。那么我们可以在做完逆FWT变换后再除以2m而不是在做逆FWT时/2.由于这么做相当于把答案*2m,所以模数也要相应地乘上2^m。

这样就可以切掉这题啦啦啦!!!

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>using namespace std;typedef long long LL;const int N=300005;int n,m,p,bin[20];char ch[1005];LL c[N],f[N],b[N],phip,t;int read(){    int x=0,f=1;char ch=getchar();    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}LL READ(LL mo){    scanf("%s",ch);int flag=0,len=strlen(ch);LL now=0;    for (int i=0;i<len;i++)    {        now=now*10+ch[i]-'0';        if (now>=mo) flag=1;        now%=mo;    }    if (flag) return now+mo;    else return now;}LL mul(LL x,LL y,LL mo){    LL tmp=(x*y-(LL)((double)x*y/mo+0.1)*mo)%mo;    if (tmp<0) tmp+=mo;    return tmp;}LL ksm(LL x,LL y,LL mo){    LL ans=1;    while (y)    {        if (y&1) ans=mul(ans,x,mo);        x=mul(x,x,mo);y>>=1;    }    return ans;}int cnt(int x){    int ans=0;    while (x) ans++,x-=x&(-x);    return ans;}int get_phi(int n){    int w=n,tmp=n;    for (int i=2;i*i<=n;i++)        if (tmp%i==0)        {            w=w/i*(i-1);            while (tmp%i==0) tmp/=i;        }    if (tmp>1) w=w/tmp*(tmp-1);    return w;}void fwt(LL *a,int n,LL mo){    for (int i=1;i<n;i<<=1)        for (int j=0;j<n;j+=(i<<1))            for (int k=0;k<i;k++)            {                LL x=a[j+k],y=a[j+k+i];                a[j+k]=(x+y)%mo;a[j+k+i]=(x-y+mo)%mo;            }}void dwt(LL *a,int n,LL mo){    for (int i=1;i<n;i<<=1)        for (int j=0;j<n;j+=(i<<1))            for (int k=0;k<i;k++)            {                LL x=a[j+k],y=a[j+k+i];                a[j+k]=(x+y)%mo;a[j+k+i]=(x-y+mo)%mo;            }}int main(){    m=read();p=read();    bin[0]=1;    for (int i=1;i<=m;i++) bin[i]=bin[i-1]*2;    phip=get_phi(p);    if (p%2==0) phip=(LL)phip*bin[m];    else phip=(LL)phip*bin[m-1];    t=READ(phip);    for (int i=0;i<bin[m];i++) f[i]=read();    for (int i=0;i<=m+1;i++) b[i]=read();    for (int i=0;i<bin[m];i++) c[i]=b[cnt(i)];    fwt(c,bin[m],(LL)p*bin[m]);    fwt(f,bin[m],(LL)p*bin[m]);    for (int i=0;i<bin[m];i++) c[i]=ksm(c[i],t,(LL)p*bin[m]);    for (int i=0;i<bin[m];i++) f[i]=mul(f[i],c[i],(LL)p*bin[m]);    dwt(f,bin[m],(LL)p*bin[m]);    for (int i=0;i<bin[m];i++) printf("%lld\n",f[i]/bin[m]);    return 0;}
原创粉丝点击