P4588 [TJOI2018]数学计算 题解

题目传送门

题意简述

\(x\) 初值为 \(1\),要求支持以下操作:

操作 1:将 \(x\leftarrow x\cdot m\),输出 \(x \mod M\)

操作 2:将 \(x\leftarrow x \div k\)\(k\) 是第 \(m\) 次操作时乘上的数,输出 \(x\mod M\)

分析

线段树好题!线段树好题!线段树好题!

做过这道题后,我真的对线段树有了更加深厚的了解。

看到这个题目其实很难想象可以使用线段树来做,但我们可以发现,如果我们按照时间轴,即 \(1\sim Q\) 建一棵线段树,第 \(1\) 个数即为 \(x\),一开始全部赋初值 \(1\)

\(i\) 个操作为 1 时就单点修改 \(i\leftarrow m\),这样 push_up时就可以将 \(x\leftarrow x \cdot m\)

为 2 时可以单点修改 \(i\leftarrow 1\),这样 push_up时,\(x\) 就相当于除以了 \(k\)(第 \(m\) 次操作乘上的数)。

每次查询,输出 tr[1].sum即可。

这样一分析,就变成了线段树裸题了(甚至还简单,都不用区间查询)。

代码实现

#include <bits/stdc++.h>
#define int long long
using namespace std;

inline int read(){
    int x=0,f=0;char ch=getchar();
    while(!isdigit(ch))f^=!(ch^45),ch=getchar();
    while(isdigit(ch))x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return f?-x:x;
}
inline void write(int x){
    if(x<0)x=-x,putchar('-');
    if(x>=10)write(x/10);
    putchar(x%10+'0');
}
inline void writeln(int x){write(x);puts("");}

int T,Q,mod;

struct segment{
    int l,r,sum;
}tr[100005<<2];
inline int ls(int p){return p<<1;}
inline int rs(int p){return p<<1|1;}
inline void push_up(int p){tr[p].sum=(tr[ls(p)].sum*tr[rs(p)].sum)%mod;}
void build(int p,int l,int r){
    tr[p]={l,r,1};
    if(l==r)return;
    int mid=l+r>>1;
    build(ls(p),l,mid);
    build(rs(p),mid+1,r);
    push_up(p);
}

void update(int p,int x,int k){
    if(tr[p].l==tr[p].r){
        tr[p].sum=k;
        return;
    }
    int mid=tr[p].l+tr[p].r>>1;
    if(x<=mid)update(ls(p),x,k);
    if(mid<x)update(rs(p),x,k);
    push_up(p);
}

signed main(){
    T=read();
    while(T--){
        Q=read();mod=read();
        memset(tr,0,sizeof tr);
        build(1,1,Q);
        for(int i=1;i<=Q;i++){
            int p=read(),m=read();
            if(p==1)update(1,i,m),writeln(tr[1].sum%mod);
            else update(1,m,1),writeln(tr[1].sum%mod);
        }
    }
    #ifndef ONLINE_JUDGE
    system("pause");
    #endif
    return 0;
}
posted @ 2022-02-20 20:03  tmjyh09  阅读(37)  评论(0编辑  收藏  举报