》》example2
解题思路
首先,假设所有操作修改的数字都是一样的,那么无论 l,rl,r 怎么取值,每一个删除操作,删除的都是固定的加入操作。
考虑预处理出每个删除操作对应的加入操作的时间点。(我处理的很麻烦,排序+栈)
线段树:单点修改区间查询
考虑把查询的区间按右端点排序,每次按时间线向后更新线段树,若当前时间点是加入操作就在把当前时间点修改为x,如果是删除,若曾经加入过这个点,就在线段数上把这个删除操作对应的加入操作的值修改为1,这样在乘的时候就相当于乘了1,也就是删掉了这个数。
查询的时候把一段区间中所有数乘起来就行了
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int n,m,l,r,v[300010];
ll p,kj[300010];
struct c{
int opt,id,t,l,r;
ll x;
}a[300010],s[300010],aa[300010];
struct cc{
int l,r;
ll x;
}tree[20001000];
bool cmp(c l,c r)
{
if(l.x==r.x)return l.t<r.t;
return l.x<r.x;
}
bool cmp1(c l,c r)
{
return l.r<r.r;
}
stack<int>s1;
void build(int k,int l,int r){
tree[k].l=l,tree[k].r=r;
tree[k].x=1;
int mid=(l+r)/2;
if(l==r)
{
tree[k].x=1;
return;
}
build(k*2,l,mid);
build(k*2+1,mid+1,r);
}
void change(int k,int x,ll v){
int l=tree[k].l,r=tree[k].r;
int mid=(l+r)/2;
if(l==r)
{
tree[k].x=v;
return;
}
if(x<=mid)
change(k*2,x,v);
if(x>mid)
change(k*2+1,x,v);
tree[k].x=tree[k*2].x*tree[k*2+1].x%p;
}
ll find(int k,int x,int y){
int l=tree[k].l,r=tree[k].r;
int mid=(l+r)/2;
ll ans=1;
if(l>=x&&r<=y)
return tree[k].x%p;
if(x<=mid)
ans=ans*find(k*2,x,y)%p;
if(y>mid)
ans=ans*find(k*2+1,x,y)%p;
return ans;
}
int main(){
scanf("%d%d%lld",&n,&m,&p);
for(int i=1;i<=n;i++)
{
scanf("%d%lld",&a[i].opt,&a[i].x);
a[i].t=i;
aa[i].x=a[i].x;
aa[i].opt=a[i].opt;
}
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
{
if(a[i].x!=a[i-1].x)
{
while(!s1.empty())
s1.pop();
if(a[i].opt==1)
s1.push(a[i].t);
}
else
{
if(a[i].opt==1)
s1.push(a[i].t);
else if(!s1.empty())
{
int x=s1.top();
s1.pop();
v[a[i].t]=x;
}
}
}
build(1,1,n);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&s[i].l,&s[i].r);
s[i].id=i;
}
sort(s+1,s+m+1,cmp1);
int last=0;
for(int i=1;i<=m;i++)
{
while(s[i].r>last)
{
last++;
if(aa[last].opt==1)
change(1,last,aa[last].x);
else if(v[last])
change(1,v[last],1);
}
kj[s[i].id]=find(1,s[i].l,s[i].r);
}
for(int i=1;i<=m;i++)
printf("%lld\n",kj[i]);
}
/*
7 4 53453543
1 100000010
1 700000010
1 5554345
1 100240010
2 7
2 5554345
1 88
1 7
1 2
1 4
1 5
*/