2020牛客暑期多校训练营(第九场)E题 Groundhog Chasing Death

题意

计算
∏ i = a b ∏ j = c d g c d ( x i , y j ) \prod_{i=a}^{b}\prod_{j=c}^{d}gcd(x^i,y^j) i=abj=cdgcd(xi,yj)

0 ≤ a , b , c , d ≤ 3 e 6 , 0 < x , y ≤ 1 e 9 , a ≤ b , c ≤ d ; 0 \leq a,b,c,d \leq 3e6,0 < x,y \leq 1e9,a \leq b,c \leq d; 0a,b,c,d3e6,0<x,y1e9,ab,cd;

题解

先求出x,y的GCD,在对GCD质因子分解,对于每一个计算出的质因子V;

计算出x中有cnt1V,y中有cnt2V;

之后枚举从ab,对于枚举的一个i来说

x中有cnt1 * iv,y中有cnt2*(c -> d)个V,则V对答案的贡献为
V ∑ j = c d m i n ( c n t 1 ∗ i , c n t 2 ∗ j ) V^{\sum_{j=c}^{d}min(cnt1*i,cnt2*j)} Vj=cdmin(cnt1i,cnt2j)

暴力枚举cd肯定不行,考虑分类讨论

①cnt1 * i >= cnt2 * d

这种情况时,对于每一个j,cnt2 * j都是较小值,所以对于这种情况下:
s u m = ∑ j = c d c n t 2 ∗ j sum=\sum_{j=c}^{d}cnt2*j sum=j=cdcnt2j
根据等差数列求和公式,很容易就计算出答案;

②cnt1 * i <= cnt2 * c

这种情况时,对于每一个j,cnt1 * i都是较小值,所以对于这种情况下:
s u m = ∑ j = c d c n t 1 ∗ i sum=\sum_{j=c}^{d}cnt1*i sum=j=cdcnt1i
常数数列更加容易计算

③不是以上情况时

这种情况时,说明存在某个pos使得pos之前时都是cnt2 * j为较小值,pos及以后的j都是cnt1 * i为较小值;

这是直接二分pos存在分界位置,所以对于这种情况下:
s u m = ∑ j = c p o s − 1 c n t 2 ∗ j + ∑ j = p o s d c n t 1 ∗ i sum=\sum_{j=c}^{pos-1}cnt2*j+\sum_{j=pos}^{d}cnt1*i sum=j=cpos1cnt2j+j=posdcnt1i
对于每一个质因子都计算出该贡献,连乘即为答案;

!!!注意sum在累加过程中会爆longlong,需要用__int128存,因此快速幂幂次项也要用__int128存

代码实现

/*******************************
*   Coder : He Shuo.           *
*   Type : Original Work       *
*******************************/

#include
#include
#include
using namespace std;
typedef long long ll;
typedef __int128 LL;
const ll mo = 998244353;
ll fpow(ll a,LL b)///幂次项用__int128存
{
    ll s = 1;
    a %= mo;
    while(b)
    {
        if(b & 1)s = s * a % mo;
        a = a * a % mo;
        b >>= 1;
    }
    return s;
}

ll a,b,c,d,x,y;

vector<ll>e;///存因子

ll func(ll l,ll r,ll key,ll num)///二分寻找pos分界位置
{
    ll mid;
    while(l < r)
    {
        mid = l + r >> 1;
        if(mid * num > key)r = mid;
        else l = mid + 1;
    }
    return l;
}

int main()
{
    scanf("%lld%lld%lld%lld%lld%lld",&a,&b,&c,&d,&x,&y);

    ll gcd = __gcd(x,y);
    for(int i = 2;i * i <= gcd;++ i)///对gcd质因子分解
    {
        if(gcd % i == 0)
        {
            e.push_back(i);
            while(gcd % i == 0)gcd /= i;
        }
    }
    if(gcd > 1)e.push_back(gcd);

    ll ans = 1;
    for(int pos = 0;pos < e.size();++ pos)
    {
        ll v = e[pos];
        ll cnt1 = 0,cnt2 = 0;
        while(x % v == 0)x /= v,cnt1 ++;///对于每一个V计算出x有多少个;
        while(y % v == 0)y /= v,cnt2 ++;///对于每一个V计算出y有多少个;
        LL sum = 0;///!!sum会爆longlong,用__int128存;
        for(ll i = a;i <= b;i ++)
        {
            LL res = 0;
            if(cnt1 * i >= cnt2 * d)res = (cnt2 * c + cnt2 * d) * (d - c + 1) / 2;///第一种情况,等差数列求和公式计算~
            else if(cnt1 * i <= cnt2 * c)res = (d - c + 1) * (cnt1 * i);///第二种情况,常数列求和简简单单~
            else///第三种情况,找到pos分界位置之后,简单计算一下~
            {
                ll pos = func(c,d,cnt1 * i,cnt2);
                res = (cnt2 * c + cnt2 * (pos - 1)) * (pos - c) / 2 + (d - pos + 1) * (cnt1 * i);
            }
            sum += res;
        }
        ans = (ans * fpow(v,sum)) % mo;///累乘计算答案~
    }
    printf("%lld",ans);
}///写完提交,Accepted你值得拥有~


你可能感兴趣的:(2020牛客多校)