[Luogu] P4588 [TJOI2018]数学计算
Solutions Luogu 线段树
Lastmod: 2020-09-18 周五 21:08:04

P4588 [TJOI2018]数学计算

题目大意

给定 $x = 1$ 和某个模数 $MO$,有两种操作(共 $Q \le 10^5$ 次)

操作 $1$:把 $x = x*v % MO$

操作 $2$:取消第 $k$ 次操作(取消的必为操作 $1$,且某个操作 $1$ 只会最多被取消一次)

每次操作结束输出 $x$ 的值。

简要题解

因为模数不是固定的,因此处理模下除法是不方便的,因此直接用线段树维护所有操作即可。

最初把整个区间初始化为 $1$。

对于第 $i$ 次操作:

如果是操作 $1$,就把 $i$ 位置设置为 $v$

如果是操作 $2$,就把 $x$ 位置设置为 $1$

某次操作完后的结果就是整个区间的积。

其实题目中的输入是不会爆 $int$ 的,但是两个 $int$ 模下的积有可能爆,只要注意处理积的就可以了。

复杂度

$T$:$O(N \log N)$

$S$:$O(N)$

代码实现

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

typedef long long LL;
const int M=1e5+5;

int MO;

int mul(int x,int y)
{
    return 1LL*x*y%MO;
}

struct SegT
{
    int tn[M*4];
    void init()
    {
        for(int i=0;i<M*4;i++)
            tn[i]=1;
    }
    int P,V;
    void update(int o,int l,int r)
    {
        if(l==r)
        {
            tn[o]=V;
            return;
        }
        int mid=(l+r)>>1;
        if(P<=mid) update(o<<1,l,mid);
        else update(o<<1|1,mid+1,r);
        tn[o]=mul(tn[o<<1],tn[o<<1|1]);
    }
}segt;

void solve()
{
    int q;
    scanf("%d%d",&q,&MO);
    segt.init();
    int con;
    int x;
    for(int i=1;i<=q;i++)
    {
        scanf("%d%d",&con,&x);
        // cout<<x<<" "<<x%MO<<endl;
        if(con==1)
        {
            segt.P=i;
            segt.V=(int)x%MO;
            segt.update(1,1,q);
        }
        else
        {
            segt.P=x;
            segt.V=1%MO;
            segt.update(1,1,q);
        }
        printf("%d\n",segt.tn[1]);
    }
}

int main()
{
    int t; scanf("%d",&t);
    while(t--)
        solve();
    return 0;
}
Prev: 《汇编语言》 笔记 第 2 章 寄存器
Next: 《汇编语言》 笔记 实验 1 查看 CPU 和内存,用机器指令和汇编指令编程