P4588 [TJOI2018] 数学计算 题解
补了一道很久之前不会做的题()
采用线段树分治,以时间为轴建立一颗线段树。初始时让每个叶子结点都为\(1\),然后维护区间乘积。
假定我们已经进行到了第\(i\)次操作
如果类型是操作1,那么就将\(i\)位置的值修改为\(m\),并且进行pushup维护区间乘积。
如果类型是操作2,那么就将\(pos\)位置的值修改为\(1\),并且进行pushup维护区间乘积。
为什么这么做是可行的呢,首先\([i+1,q]\)区间内操作在线段树上对应的叶子节点为\(1\),区间乘积的时候他们不会对结果产生影响。然后对于\([1,i]\)区间内的操作,叶子节点的数值表示要进行乘积运算的数.如果是通过操作2来除以\(pos\)位置的值,那么可以认为,这个操作没有添加,即乘以一个数再除以一个数结果不变,所以说我们把这个操作对应的叶节点置为1,表示在之后的操作当中他们不会对结果产生影响,而那些操作1对应的叶节点还没有被操作2变为1的节点会继续产生贡献。
代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+10;
ll mul[N<<2];
ll q,mod;
void up(int i){
mul[i]=mul[i<<1]*mul[i<<1|1]%mod;
}
void build(int i,int l,int r){
if(l==r){
mul[i]=1;
}else{
int mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
up(i);
}
}
void change(int ji,int jv,int i,int l,int r){
if(l==r){
mul[i]=jv;
}else{
int mid=(l+r)>>1;
if(ji<=mid){
change(ji,jv,i<<1,l,mid);
}else{
change(ji,jv,i<<1|1,mid+1,r);
}
up(i);
}
}
void sol(){
cin>>q>>mod;
build(1,1,q);
for(int i=1;i<=q;i++){
int op;
cin>>op;
if(op==1){
int m;cin>>m;
change(i,m,1,1,q);
}else{
int pos;cin>>pos;
change(pos,1,1,1,q);
}
cout<<mul[1]<<'\n';
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int t;
cin>>t;
while(t--){
sol();
}
return 0;
}

浙公网安备 33010602011771号