Groundhog Chasing Death 2020牛客多校第九场(质因子分解+欧拉降幂)

原题题面

As we all know,“Groundhog chasing death” means “GCD”,while “GCD” stands for “greatest common divisor”.
So you need to calculate ∏ i = a b ∏ j = c d gcd ⁡ ( x i , y j ) \prod_{i=a}^b\prod_{j=c}^d\gcd(x^i,y^j) i=abj=cdgcd(xi,yj) modulo 998244353 998244353 998244353

输入格式

One line which contains six intergers a , b , c , d , x , y a,b,c,d,x,y a,b,c,d,x,y.
0 ≤ a , b , c , d ≤ 3 e 6 , a ≤ b , c ≤ d , 0 ≤ x , y ≤ 1 e 9 0\leq a,b,c,d\leq 3e6,a\leq b,c\leq d,0\leq x,y \leq 1e9 0a,b,c,d3e6,ab,cd,0x,y1e9

输出格式

One line which contains ∏ i = a b ∏ j = c d gcd ⁡ ( x i , y j ) \prod_{i=a}^b\prod_{j=c}^d\gcd(x^i,y^j) i=abj=cdgcd(xi,yj) modulo 998244353 998244353 998244353

输入样例1

1 2 1 2 8 4

输出样例1

2048

输入样例2

1 2 3 4 120 180

输出样例2

235140177

题面分析

第一眼以为是莫反就屁颠屁颠地化式子去了,果然画不出来
首先我们给出gcd的另一种定义,
对于 a = ∏ i = 1 m p i x i , b = ∏ i = 1 n p i y i a=\prod_{i=1}^{m}p_{i}^{x_i},b=\prod_{i=1}^{n}p_{i}^{y_i} a=i=1mpixi,b=i=1npiyi
g c d ( a , b ) = ∏ i = 1 m i n ( m , n ) p i m i n ( x i , y i ) gcd(a,b)=\prod_{i=1}^{min(m,n)}p_{i}^{min(x_i,y_i)} gcd(a,b)=i=1min(m,n)pimin(xi,yi)
证明略,稍加理解即可。
那我们可以考虑去计算每个质因子 p i p_i pi对答案的贡献。
把原式的 x , y x,y x,y质因子分解后,对于某一个质因子 p p p,设 x x x对应的次数为 k 1 k_1 k1, y y y对应的次数为 k 2 k_2 k2
那么 p i p_i pi的贡献就是 p i ∑ i = 1 b ∑ j = c d m i n ( k 1 i , k 2 j ) p_i^{\sum_{i=1}^{b}\sum_{j=c}^{d}min(k_1i,k_2j)} pii=1bj=cdmin(k1i,k2j)
对于 ∑ i = 1 b ∑ j = c d m i n ( k 1 i , k 2 j ) \sum_{i=1}^{b}\sum_{j=c}^{d}min(k_1i,k_2j) i=1bj=cdmin(k1i,k2j)
根据 m i n min min的取值不同,我们分成两种情况。
k 1 i ≤ k 2 j k_1i\leq k_2j k1ik2j
此时可以得到 j ≥ k 1 k 2 i j\geq\frac{k_1}{k_2}i jk2k1i,所以这部分求和的结果是 ∑ i = a b ∑ j = m a x ( c , ⌈ k 1 k 2 i ⌉ ) d k 1 i \sum_{i=a}^{b}\sum_{j=max(c,\lceil\frac{k_1}{k2}i\rceil)}^{d}k_1i i=abj=max(c,k2k1i)dk1i
化简得 k 1 ∑ i = a b i ∗ [ d − m a x ( c , ⌈ k 1 k 2 i ⌉ ) + 1 ] k_1\sum_{i=a}^{b}i*[d-max(c,\lceil\frac{k_1}{k_2}i\rceil)+1] k1i=abi[dmax(c,k2k1i)+1]
k 1 i > k 2 j k_1i>k_2j k1i>k2j
此时可以得到 i > k 2 k 1 j i>\frac{k_2}{k_1}j i>k1k2j,所以这部分求和的结果是 ∑ j = c d ∑ i = m a x ( a , ⌈ k 1 k 2 j ⌉ ) b k 2 j \sum_{j=c}^{d}\sum_{i=max(a,\lceil\frac{k_1}{k2}j\rceil)}^{b}k_2j j=cdi=max(a,k2k1j)bk2j
同理得到 k 2 ∑ j = c d j ∗ [ b − m a x ( a , ⌊ k 2 k 1 j ⌋ + 1 ) + 1 ] k_2\sum_{j=c}^{d}j*[b-max(a,\lfloor\frac{k_2}{k_1}j\rfloor+1)+1] k2j=cdj[bmax(a,k1k2j+1)+1]
所以 ∑ i = 1 b ∑ j = c d m i n ( k 1 i , k 2 j ) \sum_{i=1}^{b}\sum_{j=c}^{d}min(k_1i,k_2j) i=1bj=cdmin(k1i,k2j)
= k 1 ∑ i = a b i ∗ [ d − m a x ( c , ⌈ k 1 k 2 i ⌉ ) + 1 ] + k 2 ∑ j = c d j ∗ [ b − m a x ( a , ⌊ k 2 k 1 j ⌋ + 1 ) + 1 ] =k_1\sum_{i=a}^{b}i*[d-max(c,\lceil\frac{k_1}{k_2}i\rceil)+1]+k_2\sum_{j=c}^{d}j*[b-max(a,\lfloor\frac{k_2}{k_1}j\rfloor+1)+1] =k1i=abi[dmax(c,k2k1i)+1]+k2j=cdj[bmax(a,k1k2j+1)+1]
因为质因子的个数很小,前9个质数相乘就已经超出 1 e 9 1e9 1e9了,故枚举质因子复杂度为 O ( 9 ) O(9) O(9)
暴力计算贡献的复杂度是 O ( 3 e 6 ) O(3e6) O(3e6)
质因子分解的复杂度最大是 O ( 32 ) O(32) O(32)(即 x = 2 32 x=2^{32} x=232的时候)
因此总复杂度为 O ( 9 × 3 e 6 × 32 ) O(9\times3e6\times32) O(9×3e6×32)(实际复杂度远比这小)

AC代码(1175ms)

值得注意的地方有两个:
1.要使用欧拉降幂加速
2.使用欧拉降幂时注意模数是 p − 1 p-1 p1不是 p p p(具体的请看代码)

#include<bits/stdc++.h>
using namespace std;
const long long MAXN=2e5;
const long long mod=998244353;
int prime[MAXN];//素数数组
bool is_prime[MAXN+10];//is_pri[i]表示i是素数
int factorX[MAXN+10];
int sumX=0;
int sumY=0;
int factorY[MAXN+10];
long long a, b, c, d, x, y;
int sumPrime;
//返回n以内素数的个数
inline long long quick_pow(long long a, long long b)//快速幂
{
    long long ans=1, base=a;
    while(b!=0)
    {
        if (b&1)
            ans=(long long) ans*base%mod;
        base=(long long) base*base%mod;
        b>>=1;
    }
    return ans;
}
int sieve(int n)
{
    int p=0;//素数个数计数器
    for(int i=0; i<=n; i++)
        is_prime[i]=true;
    is_prime[0]=is_prime[1]=false;//首先标记0和1不是素数
    is_prime[2]=true;//标记2是素数
    for(int i=2; i<=sqrt(n); i++)
    {
        if (is_prime[i]) //如果i是素数
        {
            for(int j=i*i; j<=n; j+=i)//所有i的倍数都不是素数
                is_prime[j]=false;
        }
    }
    for(int i=1; i<=n; i++)
        if (is_prime[i])
            prime[++p]=i;
    return p;
}
void init()//质因子分解
{
    for(int i=1; i<=sumPrime && x!=1; i++)
    {
        if (x%prime[i]==0)
        {
            sumX=i;
            while(x!=1 && x%prime[i]==0)
            {
                x/=prime[i];
                factorX[i]++;
            }
        }
    }
    if (x!=1)
    {
        sumPrime++;
        prime[sumPrime]=x;
        factorX[sumPrime]++;
    }
    for(int i=1; i<=sumPrime && y!=1; i++)
    {
        if (y%prime[i]==0)
        {
            sumY=i;
            while(y!=1 && y%prime[i]==0)
            {
                y/=prime[i];
                factorY[i]++;
            }
        }
    }
    if (y!=1)
    {
        sumPrime++;
        prime[sumPrime]=y;
        factorY[sumPrime]++;
    }
}
void solve()
{
    sumPrime=sieve((int) 2e5);
    scanf("%lld%lld%lld%lld%lld%lld", &a, &b, &c, &d, &x, &y);
    init();
    long long ans=1;
    for(int z=1; z<=min(sumX, sumY); z++)
    {
        if (factorX[z] && factorY[z])
        {
            long long powP1=0, powP2=0;
            for(long long i=a; i<=b; i++)
            {
                long long j;
                if(factorX[z]*i%factorY[z]==0)
                {
                    j=(long long)max(factorX[z]*i/factorY[z],c);
                }
                else
                {
                    j=(long long)max(factorX[z]*i/factorY[z]+1,c);
                }
                if (j<=d && j>=0)
                    powP1=(powP1+(long long) i*((d-j+1+(mod-1))%(mod-1))%(mod-1))%(mod-1);
            }
            powP1=(long long) powP1*factorX[z]%(mod-1);
            for(long long i=c; i<=d; i++)
            {
                long long j;
                j=(long long) max(factorY[z]*i/factorX[z]+1, a);
                if (j<=b && j>=0)
                    powP2=(powP2+(long long) i*((b-j+1+(mod-1))%(mod-1))%(mod-1))%(mod-1);
            }
            powP2=(long long) powP2*factorY[z]%(mod-1);
            ans=(long long) (ans*quick_pow(prime[z], (powP1+powP2)%(mod-1)))%mod;
        }
    }
    printf("%lld\n", ans);
}
int main()
{
//    ios_base::sync_with_stdio(false);
//    cin.tie(0);
//    cout.tie(0);
#ifdef ACM_LOCAL
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    long long test_index_for_debug=1;
    char acm_local_for_debug;
    while(cin>>acm_local_for_debug)
    {
        cin.putback(acm_local_for_debug);
        if (test_index_for_debug>100)
        {
            throw runtime_error("Check the stdin!!!");
        }
        auto start_clock_for_debug=clock();
        solve();
        auto end_clock_for_debug=clock();
        cout<<"\nTest "<<test_index_for_debug<<" successful"<<endl;
        cerr<<"Test "<<test_index_for_debug++<<" Run Time: "
            <<double(end_clock_for_debug-start_clock_for_debug)/CLOCKS_PER_SEC<<"s"<<endl;
        cout<<"--------------------------------------------------"<<endl;
    }
#else
    solve();
#endif
    return 0;
}

后记

赛中傻逼了,欧拉降幂模数一直错,白白WA了3发…
DrGilbert 2020.8.9

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值