题目链接:点击查看
题目大意:给出 a , b , c , d , x , y ,求
题目分析:因为涉及到了 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 就是 ,也就是
,这也就提醒我们可以对指数单独处理,最后求一下 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("");
}
}
这里不卖关子了,直接说规律:
- 当 i 确定时,[ c , d ] 的一列可以分为两段:
- 前半段为等差数列
- 后半段为常数
- 当 j 确定时,[ a , b ] 的一行可以分为两段:
- 前半段为等差数列
- 后半段为常数
这样就可以将 ( 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;
}