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

posted @ 2026-03-06 23:26  1445141  阅读(15)  评论(0)    收藏  举报