差分思想摘录博客:https://www.cnblogs.com/COLIN-LIGHTNING/p/8436624.html
差分数组介绍:
数组f[5]={1,6,3,5,7},他的差分数组就是d[5]={1,5,-3,2,2};
即d[i]=f[i]-f[i-1] (f[0]=0),f[i]=d[i]+d[i-1]+···+d[1];
差分数组定义:
对于已知有n个元素的离线数列d,我们可以建立记录它每项与前一项差值的差分数组f:
显然,f[1]=d[1]-0=d[1] ; 对于整数i∈[2,n],我们让f[i]=d[i]-d[i-1]。
差分数组性质:
(1)计算数列各项的值:观察d[2]=f[1]+f[2]=d[1]+d[2]-d[1]=d[2]可知,数列第i项的值是可以用差分数组的前i项的和计算的,即d[i]=f[i]的前缀和。
(2)计算数列每一项的前缀和:第i项的前缀和即为数列前i项的和,即可用差分数组求出数列前缀和;
用途:
(1)快速处理区间加减操作:
假如现在对数列中区间[L,R]上的数加上x,我们通过性质(1)知道,第一个受影响的差分数组中的元素为f[L],即令f[L]+=x,那么后面数列元素在计算过程中都会加上x;最后一个受影响的差分数组中的元素为f[R],所以令f[R+1]-=x,即可保证不会影响到R以后数列元素的计算。这样我们不必对区间内每一个数进行处理,只需处理两个差分后的数即可;
(2)询问区间和问题:
由性质(2)我们可以计算出数列各项的前缀和数组sum各项的值;那么显然,区间[L,R]的和即为ans=sum[R]-sum[L-1];
Master of GCD
时间限制: 1 Sec 内存限制: 128 MB
题目描述
Hakase有一个数字。首先,它们都等于1.此外,Hakase对素数感兴趣。她将每次选择一个连续的子序列[l,r]和一个素数参数x,并且对于每个l≤i≤r,她将ai变为ai * x。为了简化问题,x将是2或3.在m次操作之后,Hakase想知道所有数字的最大公约数是多少。
输入
第一行包含表示测试用例数的整数T(1≤T≤10)。
对于每个测试用例,第一行包含两个整数n(1≤n≤100000)和m(1≤m≤100000),其中n表示整个序列的长度,m表示有m个运算。
以下m行,每行包含三个整数li(1≤li≤n),ri(1≤ri≤n),xi(xi∈{2,3}),如上所述。
输出
对于每个测试用例,在一行中打印一个整数,表示序列的最大公约数。由于答案可能非常大,打印答案模998244353。
样例输入
复制样例数据
5 3
1 3 2
3 5 2
1 5 3
6 3
1 2 2
5 6 2
1 6 2
样例输出
6
2
提示
对于第一个测试用例,在所有操作之后,数字将是[6,6,12,6,6]。所以最大的公约数是6。
题解:
本题用树状数组差分思想更新和计算前缀和,计算出现的2,3的最小次数
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<math.h>
#include<queue>
#include<bitset>
typedef long long ll;
const int INF=1e9+7;
const int xmax=1e5+7;
const double PI=acos(-1);
const int mod=998244353;
using namespace std;
ll n,m,l,r,d;
ll sum2[xmax],sum3[xmax];
ll lowbit(ll x){
return x&(-x);
}
///更新
void update(ll x,ll k,ll sum[]){
while(x<xmax){
sum[x]+=k;
x+=lowbit(x);
}
}
///求和
ll compute(int x,ll sum[]){
ll ans=0;
while(x>0){
ans+=sum[x];
x-=lowbit(x);
}
return ans;
}
///快速幂
ll qpow(ll a,ll b){
ll ans=1;
while(b){
if(b&1) ans=(ans*a)%mod;
a=a*a%mod;
b>>=1;
}
return ans%mod;
}
int main()
{
int t;
cin>>t;
while(t--){
memset(sum2,0,sizeof(sum2));
memset(sum3,0,sizeof(sum3));
cin>>n>>m;
while(m--){
cin>>l>>r>>d;
if(d==2){
///差分思想更新[l,r]内所有的值+1,这里的+1是出现的次数
///从l到最后都+1,再从r+1到最后都-1,实现从[l,r]区间更新
update(l,1,sum2);
update(r+1,-1,sum2);
}
else if(d==3){
update(l,1,sum3);
update(r+1,-1,sum3);
}
}
ll minn1=INF;
ll minn2=INF;
for(ll i=1;i<=n;i++){
///compute()是求前缀和,即i这个点的*2或*3的次数,并找到最小值
minn1=min(minn1,compute(i,sum2));
minn2=min(minn2,compute(i,sum3));
}
minn1=qpow(2,minn1);
minn2=qpow(3,minn2);
ll ans=(minn1*minn2)%mod;
cout<<ans<<endl;
}
return 0;
}