牛客多校9 - Groundhog Chasing Death(质因子分解+思维)

题目链接:点击查看

题目大意:给出 a , b , c , d , x , y ,求\prod_{i=a}^{b}\prod_{j=c}^{d}gcd(x^i,y^j)

题目分析:因为涉及到了 gcd 的乘积运算,那么易知不同质因子的贡献是相互独立的,首先我们就可以先将 x 和 y 进行质因子分解,那么对于质因子 p 来说,设 cntx[ p ] 为 p 在 x 中出现的次数,cnty[ p ] 为 p 在 y 中出现的次数,不难看出,需要这两个数同时大于 0 才有贡献,如果其中一者为 0 的话,那么其表示的质因子就是 p^0 = 1 ,gcd 求出来显然也就是 1 了,对答案没有贡献

到此,cntx[ p ] 和 cnty[ p ] 都大于 0 ,那么质因子 p 的 gcd 就是 gcd(p^{cntx[p]},p^{cnty[p]}),也就是 p^{min(cntx[p],cnty[p])},这也就提醒我们可以对指数单独处理,最后求一下 p 的幂次再乘起来就是答案了

此时我们可以对指数稍微打表找一下规律,打表程序如下(可以自己更改一下 i 和 j 的取值范围,分别代表 [ a , b ] 和 [ c , d ] ):

for(int k=1;k<=5;k++)//质因子p在x中的个数 
	for(int t=1;t<=5;t++)//质因子p在y中的个数 
	{
		printf("*****cntx:%d cnty:%d*****\n",k,t);
		for(int i=1;i<=5;i++)//a~b
		{
			for(int j=1;j<=5;j++)//c~d 
				printf("%d ",min(i*k,j*t));
			puts("");
		}
	}

这里不卖关子了,直接说规律:

  1. 当 i 确定时,[ c , d ] 的一列可以分为两段:
    1. 前半段为等差数列
    2. 后半段为常数
  2. 当 j 确定时,[ a , b ] 的一行可以分为两段:
    1. 前半段为等差数列
    2. 后半段为常数

这样就可以将 ( b - a + 1 ) * ( d - c + 1 ) 的时间复杂度优化为 ( b - a + 1 ) 或者 ( d - c + 1 ) 了,因为知道了是等差数列,我们可以求出三项:第一项,第二项,最后一项,根据第一项和第二项得出公差,根据第一项、最后一项和公差计算出等差数列的长度,这样最后常数项的长度也能计算得出了

有个小坑就是,如果指数直接进行运算的话,会爆 long long ,可以用费马小定理降幂,一方面是保证指数在数据范围内,另一方面是加速快速幂的运算

因为我用了 map 参与质因子分解,所以总的时间复杂度为 ( b - a + 1 ) * logn * logn

代码:
 

#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<unordered_map>
using namespace std;

typedef long long LL;

typedef unsigned long long ull;

const int inf=0x3f3f3f3f;

const int N=1e5+100;

const int mod=998244353;

const int mmod=mod-1;

map<int,int>mpx,mpy;

LL q_pow(LL a,LL b)
{
	LL ans=1;
	while(b)
	{
		if(b&1)
			ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}

void only(map<int,int>&mp,int x)
{
	for(int i=2;i*i<=x;i++)
	{
		while(x%i==0)
		{
			mp[i]++;
			x/=i;
		}
	}
	if(x!=1)
		mp[x]++;
}

int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
	int a,b,c,d,x,y;
	scanf("%d%d%d%d%d%d",&a,&b,&c,&d,&x,&y);
	a=max(a,1);
	c=max(c,1);
	only(mpx,x),only(mpy,y);
	LL ans=1;
	for(auto it:mpx)
	{
		int num=it.first,cntx=it.second,cnty=mpy[num];
		if(cntx==0||cnty==0)
			continue;
		LL res=0;//指数 
		for(int i=a;i<=b;i++)
		{
			LL mmin=min(1LL*i*cntx,1LL*c*cnty),mmax=min(1LL*i*cntx,1LL*d*cnty);//第一项和最后一项
			LL delta=min(1LL*i*cntx,1LL*(c+1)*cnty)-mmin;//公差
			LL k=d-c+1;//一共有k项
			LL n;//有几项是等差数列
			if(delta==0)
				n=k;
			else
				n=(mmax-mmin)/delta+1;
			LL sum=(n*mmin%mmod+(n*(n-1)/2)%mmod*delta%mmod)%mmod;//等差数列求和公式
			res=(res+sum%mmod+1LL*(k-n)*mmax%mmod)%mmod;
		}
		ans=ans*q_pow(num,res)%mod;
	}
	printf("%lld\n",ans);






    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/107888920
今日推荐