gcdlcm[组合数学]

gcdlcm[组合数学]:

描述:
问有多少个 k 元组(a1,a2,a3,...,ak)(ai>=1)满足 gcd(a1,a2,...,ak)=D,
并且 lcm(a1,a2,a3,...,ak)=L。
输入:
第一行有三个整数 k,D,L 分别为题面所描述的。
输出:
输出答案 mod(1e9+7)
输入输出样例:
gcdlcm.in gcdlcm.out
2 1 6 4
数据范围:
对于 30%数据 2<=k<=100,D<=100,L<=100
对于 100%数据 2<=k<=1000000000 D<=1000000000 L<=1000000000


数学弱成渣了,毫无办法- -

---------------------------------------------------------------------------------------------------------------------------------------------------------

首先,D|L.  否则不合法,输出0.

然后,将D,L分解为D=p1^b1*p2^b2*p3^b3*```*pq^bq  ,L=p1^c1*p2^c2*p3^c3*````*pq^cq

然后选k个数出来,使得k个数分解出来的cnt[i][pj]满足:

cj>=(cnt[i][pj])>=bj        ,求方案数

考虑对于每个因子pi   我们要在[bi,ci]里面选k个,并且满足min(pij)=bi,max(pij)=ci.得到一个方案gi

然后把所有gi乘起来!


问题就转化为求gi了!

这里要用到数学的一个容斥:(数学太弱的我表示不会想到)

合法条件在pi中一定至少有一个选了bi,ci个.  不合法的就是 没有选bi个+没有选ci个-(没有选bi&&ci个)

我们考虑用总的减去不合法的

总的就是在[bi,ci]中选k个,为(ci-bi+1)^k

不选bi或者不选ci都是(ci-bi)^k

gi=(ci-bi+1)^k-2*(ci-bi)^k+(ci-bi-1)^k

这个用快速幂求一下就好了


复杂度其实不算很大,不过需要考虑分解出来的pq可能会非常大(如果>sqrt(n)就单独记录,因为数组开不下)

代码:

#include<cstdio>
#include<iostream>
#include<queue>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#define LL long long
using namespace std;
int k;
int D,L;
const LL M=1e9+7;
/*gcd(a1,a2,a3---an)=D=p1^b1 * p2^b2 * p3^b3
//lcm(a2,a2,a3---an)=L=p1^c1 * p2^c2 * p3^c3                  pk
//选法就是考虑每个因子pi 从[b1,c1]中选k个 得到方案数 gi   ans=π gi
                                                            //i=1 
对于每个gi,我们考虑从[b1,c1]中选k个,方案数 (c1-b1+1)^k
合法状态一定有至少一个b1,c1
那么不合法状态,  不选b1  ---(b1,c1]选k个,方案数  (c1-b1)^k
                 不选c1  ---[b1,c1)选k个,方案数  (c1-b1)^k
多减去了(b1,c1)的方案数  (c1-b1-1)^k
gi=(c1-b1+1)^k-2*(c1-b1)^k+(c1-b1-1)^k; 
复杂度 log max(D,L) * 
*/ 
int b[1000000],c[1000000];
int xx=-1;
int ml=0;
void fenjie(int x,int *a)//把x拆分到a数组 
{
	memset(a,0,sizeof(a));
	int t=(int)sqrt(1.0*x);
	for(int i=2;i<=t;i++)
	{
		if(x%i==0)
		{
			ml=max(ml,i);
			while(x%i==0)
			{
				x/=i;
				a[i]++;
			}
		}
	}
	if(x>1)
	{
		if(x>1000000)xx=x;
		else 
		{
			ml=max(ml,x);
			a[x]++;
		}
	}
	//
}
LL qk(LL x,LL y)//快速计算x^y mod M 
{
	LL res=1;
	while(y)
	{
		if(y%2==1)res=((res%M)*(x%M))%M;
		y/=2;
		x=((x%M)*(x%M))%M;
	}
	return res;
}
LL ans;
int main()
{
	freopen("gcdlcm.in","r",stdin);
	freopen("gcdlcm.out","w",stdout);
	scanf("%d%d%d",&k,&D,&L);
	if(L%D)printf("0\n");
	else
	{
		fenjie(D,b);
	    fenjie(L,c);
	    LL g=0;
	    ans=1;
	    for(int i=2;i<=ml;i++)
	 {
		if(!c[i])continue;
		LL d=(LL)c[i]-(LL)b[i];
		if(!d)g=1;
		else g=qk(d+1,k)%M-2*qk(d,k)%M+qk(d-1,k);
		g%=M;
		ans=((ans%M)*g)%M;
	 }
	    if(xx!=-1)
	    {
	    	if(D%xx!=0)
	    	{
	    		LL d=1;
	    	    g=qk(d+1,k)%M-2*qk(d,k)%M+qk(d-1,k);
	    	    ans=((ans%M)*(g%M))%M;
	    	}	    	
	    }
	    ans=(ans+M)%M;
	    printf("%I64d\n",ans);
	}
	
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值