Luogu P4588 数学运算 题解 [ 绿 ] [ 线段树 ]

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;
}
posted @ 2024-08-26 21:43  KS_Fszha  阅读(1)  评论(0编辑  收藏  举报