2020暑期牛客多校训练营第九场(E)Groundhog Chasing Death(质因数分解,费马小定理)

Groundhog Chasing Death

原题请看这里

题目描述:

众所周知,“土拨鼠追逐死亡”的意思是“ G C D GCD GCD”,而“ G C D GCD GCD”代表“最大公约数”。
因此,您需要计算 ∏ i = a b ∏ j = c d gcd ⁡ ( x i , y j ) \displaystyle \prod_{i=a}^b \prod_{j=c}^d \gcd(x^i,y^j) i=abj=cdgcd(xiyj)并对 998244353 {998244353} 998244353取模

输入描述:

一行包含六个整数 a , b , c , d , x , y {a,b,c,d,x,y} abcdxy ( 0 ⩽ a , b , c , d ⩽ 3 × 1 0 6 , 0 < x , y ⩽ 1 0 9 , a ⩽ b , c ⩽ d . ) (0⩽a,b,c,d⩽3×10^6,0<x,y⩽10^9,a⩽b,c⩽d.) (0a,b,c,d3×106,0<x,y109,ab,cd.)

输出描述:

一行包含 ∏ i = a b ∏ j = c d gcd ⁡ ( x i , y j ) \displaystyle \prod_{i=a}^b \prod_{j=c}^d \gcd(x^i,y^j) i=abj=cdgcd(xiyj) m o d mod mod 998244353 {998244353} 998244353

样例:

样例输入1:

1 2 1 2 8 4

样例输出1:

2048

样例输入2:

1 2 3 4 120 180

样例输出2:

235140177

思路:

首先看看数据范围(正所谓看数据猜算法 ),发现 O ( N 2 ) O(N^2) O(N2)都过不了,只有 O ( N l o g N ) , O ( N ) O(NlogN),O(N) O(NlogN)O(N)能过。
我们令
x i = p 1 a 1 ∗ p 2 a 2 ∗ p 3 a 3 ∗ . . . . . . p n a n x^i=p_1^{a1}*p_2^{a2}*p_3^{a3}*......p_n^{an} xi=p1a1p2a2p3a3......pnan
y j = q 1 b 1 ∗ q 2 b 2 ∗ q 3 b 3 ∗ . . . . . . q m b m y^j=q_1^{b1}*q_2^{b2}*q_3^{b3}*......q_m^{bm} yj=q1b1q2b2q3b3......qmbm
我们发现, x i x^i xi y j y^j yj的最大公因数一开始很快的增长,后来便不变了。假设 i < j i<j i<j,因为当 x , y x,y x,y中指数较小的一个和另一个数两两匹配完之后就只剩下指数较大的那个数了,也就是 y j − i y^{j-i} yji
然后我们发现,前 i − 1 i-1 i1 g c d gcd gcd的值依次递增,所以可以用等差数列来求,后 j − i + 1 j-i+1 ji+1 y y y x x x的最大公因数相同,可以直接计算,如果最后 x > 1 x>1 x>1,说明 x x x y y y可能还有质因数,再做一遍即可。
时间复杂度 O ( N ) O(N) O(N)

A C AC AC C o d e Code Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=998244353;
ll a,b,c,d,x,y,z,ans=1,n,u,v,md=mod-1;
ll ksm(ll a,ll b){
	ll ret=1;
	while(b){
		if(b&1)ret=ret*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ret;
}
ll get(ll x,ll y){
	ll k=0,p;
	for(int i=1;i<=x;i++){
		p=u*i/v;
		p=min(p,y);
		k=(k+(p+1ll)*p/2%md*v)%md;
		k=(k+1ll*i*(y-p)%md*u)%md;
	}
	return k;
}
int main(){
	scanf("%lld%lld%lld%lld%lld%lld",&a,&b,&c,&d,&x,&y);
	n=max(x,y);
	for(int i=2;i*i<=n;i++){
		while(x%i==0) u++,x/=i;
		while(y%i==0) v++,y/=i;
		if(u&&v){
			z=(2ll*md+get(b,d)+get(a-1,c-1)-get(a-1,d)-get(b,c-1))%md;
			ans=1ll*ans*ksm(i,z)%mod;
		}
		u=v=0;
	}
	if(x^0&&x==y){
		u=v=1;
		z=(2ll*md+get(b,d)+get(a-1,c-1)-get(a-1,d)-get(b,c-1))%md;
		ans=1ll*ans*ksm(x,z)%mod;
	}
	printf("%lld",ans);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值