Luogu P4588 数学运算 题解 [ 绿 ] [ 线段树 ]
虽然是一个很典的题,但里面的思想还是比较值得记录的。
假做法
一开始看到此题还以为是乘法逆元的模板题,但看到 \(m\) 与 \(M\) 不互质,就知道这种做法是假的了。注意 exgcd 虽然能求模数为合数的逆元,但是要是两数不互质就什么算法都搞不了了。
因此,本题不能进行任何与原来的值有关的(要做逆运算的)修改,所以树状数组之类的解法直接被毙掉了。
正解
本题的 trick:对于在正操作之后要撤销正操作(即执行反操作),且反操作较难实现的,可以选择从第一个正操作起,把所有没有被撤销的正操作算一遍,就是答案。
因此,直接用线段树维护一下区间乘法就 AC 了。
注意多测不清空,保龄两行泪。
线段树的清空,一般只要重新 build
一遍就可以了。
代码
#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
using namespace std;
typedef long long ll;
typedef pair<int,int> pi;
const int N=100005;
ll t,q,mod,m,op,a[100005],pos[100005];
struct node{
int l,r;
ll mul;
}tr[4*N];
void pushup(int p)
{
tr[p].mul=(tr[lc].mul*tr[rc].mul)%mod;
}
void build(int p,int ln,int rn)
{
tr[p]={ln,rn,a[ln]};
if(ln==rn)return;
int mid=(tr[p].l+tr[p].r)>>1;
build(lc,ln,mid);
build(rc,mid+1,rn);
pushup(p);
}
void update(int p,int x,ll v)
{
if(tr[p].l==x&&tr[p].r==x)
{
tr[p].mul=v%mod;
return;
}
int mid=(tr[p].l+tr[p].r)>>1;
if(x<=mid)update(lc,x,v);
else update(rc,x,v);
pushup(p);
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--)
{
cin>>q>>mod;
for(int i=1;i<=q;i++)a[i]=1;
build(1,1,q);
int n=0;
for(int i=1;i<=q;i++)
{
cin>>op>>m;
if(op==1)
{
m%=mod;
update(1,++n,m);
cout<<tr[1].mul%mod<<endl;
}
else
{
update(1,pos[m],1);
cout<<tr[1].mul%mod<<endl;
}
pos[i]=n;
}
}
return 0;
}