欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

J - Master of GCD ( 差分 )

程序员文章站 2022-03-13 16:37:23
...

J - Master of GCD ( 差分 )

J - Master of GCD ( 差分 )

J - Master of GCD ( 差分 )

题目描述:给你一个大小为n的数列,最开始数列的数全都为1。有m次更新,每次输入l,r,c。代表将在数列区间l到r的数成上c。最后求出整个数列gcd。

题目分析:乍一眼看过去,貌似就是一个裸的区间更新线段树维护区间的gcd。但是,很显然,直接维护gcd的操作太过于困难,因此我们退而求其次,因为每次进行的操作都是对2或3进行乘积,因此,我们可以在每次更新的过程中记录区间中2和3的个数,最后用快速幂乘起来算gcd。

因为只需要记录个数,因此,我们可以使用树状数组去维护区间的值。

统计好个数之后,还得注意一个优化:因为gcd的值事实上与2和3出现次数最多的数没有任何影响,只有出现次数最少的数才会对gcd的值有影响。因此我们只需要分别记录下2和3中出现次数最少的数,用快速幂算出他们所对应的值并相乘即是答案。

不会用树状数组用差分也可以

代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1002;
const ll mod = 998244353;
int n,m;
int a2[100005]; // 存因子2的个数
int a3[100005]; // 存因子3的个数

ll qpow( ll a, ll n )
{
    ll re = 1;
    while ( n ) {
        if ( n&1 ) {
            re = (re*a)%mod;
        }
        n >>= 1;
        a = (a*a)%mod;
    }
    return re;
}

int main()
{
    int listt;
    cin >>listt;
    while ( listt-- ) {
        cin >> n >> m;
        memset(a2,0,sizeof(a2));
        memset(a3,0,sizeof(a3));
        while ( m-- ) {
            int l,r,op;
            scanf("%d %d %d",&l,&r,&op);
            if ( op==2 ) {
                a2[l] ++;      // 差分思想
                a2[r+1] --;
            }
            else if ( op==3 ) {
                a3[l] ++;
                a3[r+1] --;
            }
        }
        int now2 = 0;
        int ans2 = 0x3f3f3f3f;
        for ( int i=1; i<=n; i++ ) {
            now2 += a2[i];
            ans2 = min(ans2,now2);
        }
        int now3 = 0;
        int ans3 = 0x3f3f3f3f;
        for ( int i=1; i<=n; i++ ) {
            now3 += a3[i];
            ans3 = min(ans3,now3);
        }
        ll ans = qpow(2,ans2);
        ans = (ans*qpow(3,ans3))%mod;
        cout << ans << endl;
    }

    return 0;
}