最近因为忙于搞密码学竞赛,做题懈怠了。需要努力。
刚刚学习了FWT,其实不陌生了,去年暑期ACM集训的时候就碰到过两次,但当时补题习惯差一直没有学习。如果了解FFT,那么FWT要解决的问题就比较容易理解了。实际上就是把FFT的多项式乘法求系数修改成多项式位运算求系数。思路和结构基本相似,也有不同之处,例如没有用到原根以及蝴蝶变换。数学证明不作罗列了,参见:https://www.cnblogs.com/cjyyb/p/9065615.html 非常详细,作者的模板也很完善了,这里再次罗列一下模板
void FWT_or(int *a, int n, int o=1)
{
for(int i=1;i<n;i<<=1)
for(int j=0;j<n;j+=i<<1)
for(int k=0;k<i;k++)
if(o==1)
a[i+j+k]=(a[j+k]+a[i+j+k])%mo;
else
a[i+j+k]=(a[i+j+k]+mo-a[j+k])%mo;
}
void FWT_and(int *a, int n, int o=1)
{
for(int i=1;i<n;i<<=1)
for(int j=0;j<n;j+=i<<1)
for(int k=0;k<i;k++)
if(o==1)
a[j+k]=(a[j+k]+a[i+j+k])%mo;
else
a[j+k]=(a[j+k]+mo-a[i+j+k])%mo;
}
void FWT_xor(int *a, int n, int o=1)
{
int x,y;
for(int i=1;i<n;i<<=1)
for(int j=0;j<n;j+=i<<1)
for(int k=0;k<i;k++)
{
x=a[j+k],y=a[i+j+k];
a[j+k]=(x+y)%mo,a[i+j+k]=(x-y+mo)%mo;
if(o==-1)
a[j+k]=(LL)a[j+k]*(mo+1)/2%mo,a[i+j+k]=(LL)a[i+j+k]*(mo+1)/2%mo;
}
}
接下来上一道例题,出自nowcoder
https://ac.nowcoder.com/acm/contest/373/F
题意看似很长,只要理解透彻就很容易。莫比乌斯函数为1说明无平方因子。也就是说所有质因子数量为1,从小到大罗列一个数的质因子,2,3,5,7……如果含有则对二进制标记为1,生成一个标记数。而g函数,实际上可以看作三个标记数的异或值的标记数再经过还原得到的结果。
每个标记数所在的下标加一得到权值数组a,b,c,显然,接下来只要做一次异或类型的FWT就可以得出结果了。
#include<cstdio>
#define mo 1000000007
#define chg(a,i) (a^=1<<i)
using namespace std;
using LL=long long;
const int pm[20]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71};
int a[1<<20],b[1<<20],c[1<<20],s,ans[1<<20],res,n,f[1<<20];
void FWT_xor(int *a, int n, int o=1)
{
int x,y;
for(int i=1;i<n;i<<=1)
for(int j=0;j<n;j+=i<<1)
for(int k=0;k<i;k++)
{
x=a[j+k],y=a[i+j+k];
a[j+k]=(x+y)%mo,a[i+j+k]=(x-y+mo)%mo;
if(o==-1)
a[j+k]=(LL)a[j+k]*(mo+1)/2%mo,a[i+j+k]=(LL)a[i+j+k]*(mo+1)/2%mo;
}
}
void init(int *a)
{
LL x;
for(int i=1,y,cnt;i<=n;i++)
{
scanf("%lld",&x);
y=0;
for(int j=0;j<20;j++)
{
cnt=0;
while(x%pm[j]==0)
x/=pm[j],cnt++;
if(cnt)
chg(y,j);
}
a[y]++;
}
}
int main()
{
scanf("%d",&n);
init(a),init(b),init(c);
FWT_xor(a,1<<20),FWT_xor(b,1<<20),FWT_xor(c,1<<20);
for(int i=0;i<1<<20;i++)
ans[i]=(LL)a[i]*b[i]%mo*c[i]%mo;
FWT_xor(ans,1<<20,-1);
f[0]=1;
for(int i=1;i<1<<20;i++)
f[i]=(LL)f[i-(i&-i)]*pm[__builtin_ctz(i)]%mo;
for(int i=0;i<1<<20;i++)
res=(res+(LL)ans[i]*f[i]%mo)%mo;
printf("%d",res);
return 0;
}