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;
}