【模板】【FWT】简单数学题

 最近因为忙于搞密码学竞赛,做题懈怠了。需要努力。
 刚刚学习了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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值