题解 P4891 【序列】
关于这一道题的话。。。
我们先从题干看起,题目要求更改A序列或者B序列,最后求一个min(Bi,Ci)的总乘积,其实要修改B的话很容易,O(1)即可。但是要修改A的话,会导致C序列大幅改变。于是这时候就有同学站出来说要用线段树了,但是答案是要一个总乘积的,就算对于C序列的修改时间复杂度降下去了,求积的部分也会很难受。
于是我们可以这么想,要是可以维护这个答案就好了,然后这道题就变成一道简单的乘法逆元版子题了。
首先,我们建立一个mul数组来维护每一位的min(Bi,Ci),sum为初始的答案。
在修改B序列时,我们直接更改B[i],然后将新的min(Bi,Ci)与原来的mul[i]比较,若有不同,则将原来的除掉,将新的乘上,然后更新mul数组。
对于A序列的修改,我们从要修改的第i位开始,把比更改的值小的C[i]全部更新成修改后的值,之后就是照葫芦画瓢。与B序列修改一样的处理方式。
当然,这道题是要模的,由于计算中涉嫌除法运算,不能直接模,所以我们就可以运用乘法逆元
(a/b) mod p == (a*b^(p-2)) mod p
下面是AC代码
#include<cstdio>
#include<algorithm>
using namespace std;
long long n,q;
long long a[100010],b[100010],c[100010],sum=1,mul[100010];
const long long mod = 1e9+7;
long long ksm(long long a){
long long summ = 1,b = mod-2;
while(b){
if(b&1)summ=(summ*a)%mod;
a = (a*a)%mod;
b>>=1;
}
return summ;
}
int main(){
scanf("%lld%lld",&n,&q);
for(long long i = 1; i <= n; i++)scanf("%lld",&a[i]);
for(long long i = 1; i <= n; i++)scanf("%lld",&b[i]);
for(long long i = 1; i <= n; i++)c[i] = max(c[i-1],a[i]);
for(long long i = 1; i <= n; i++)sum=(sum*min(c[i],b[i]))%mod,mul[i]=min(c[i],b[i]);
while(q--){
long long op,id,val;
scanf("%lld%lld%lld",&op,&id,&val);
if(op==1){
b[id] = val%mod;
if(min(c[id],b[id])!=mul[id]){
sum = ((sum*ksm(mul[id])%mod)*min(c[id],b[id]))%mod;
mul[id] = min(c[id],b[id]);
}
}
else {
while(c[id]<val){
if(id>n)break;
c[id] = val%mod;
if(min(c[id],b[id])!=mul[id]){
sum = ((sum*ksm(mul[id])%mod)*min(c[id],b[id]))%mod;
mul[id] = min(c[id],b[id]);
}
id++;
}
}
printf("%lld\n",sum%mod);
}
}