题解 CF848C Goodbye Souvenir
题目链接
题意描述
给定一个长为\(n\)的数组,定义一个数在区间\([l,r]\)的权值为这个数最后一次出现下标\(-\)第一次出现下标,要求支持单点修改,区间查询。
\(n,a_i\le10^5\)
\(\large{Solution:}\)
“最后一次-第一次”
有一个经典的\(Trick:\)
设出现下标从小到大为\(a_1,a_2…a_k\),则\(a_k-a_1=(a_k-a_{k-1})+(a_{k-1}-a_{k-2})…+(a_2-a_1)\)
于是我们可以维护相邻出现的下标差,求个和就行了。
发现一对下标\(a_i,pre_{a_i}\)计入答案的条件为\([l\le a_i\le r]\wedge[l\le pre_{a_i}\le r]\),所以只要求出每个下标的\(pre\),问题就是一个动态二维数点。
这里链表操作用的\(Treap\)实现,二维数点用树套树。
点击查看代码
#include<bits/stdc++.h>
#pragma GCC optimize(2,3,"Ofast")
#define int unsigned int
#define inf 1e18
#define N 100005
#define mid ((l+r)>>1)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define pii pair<int,int>
#define il inline
#define file(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout)
using namespace std;
il int read(){
int w=0,h=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')h=-h;ch=getchar();}
while(ch>='0'&&ch<='9'){w=w*10+ch-'0';ch=getchar();}
return w*h;
}
int n,m,a[N];
namespace Treap{
struct Data{int ls,rs,siz,cnt,val,prio;}tr[N<<2];int rt[N],tot;
int Newnode(int val){tr[++tot]={0,0,1,1,val,rand()};return tot;}
void Pushup(int k){tr[k].siz=tr[tr[k].ls].siz+tr[tr[k].rs].siz+tr[k].cnt;}
void Zig(int&k){
int p=tr[k].ls;
tr[k].ls=tr[p].rs;
tr[p].rs=k;
k=p;
Pushup(tr[k].rs);
Pushup(k);
}
void Zag(int&k){
int p=tr[k].rs;
tr[k].rs=tr[p].ls;
tr[p].ls=k;
k=p;
Pushup(tr[k].ls);
Pushup(k);
}
void Insert(int&k,int val){
if(!k)return void(k=Newnode(val));
if(val==tr[k].val)tr[k].cnt++;
if(val<tr[k].val){
Insert(tr[k].ls,val);
if(tr[tr[k].ls].prio>tr[k].prio)Zig(k);
}
if(val>tr[k].val){
Insert(tr[k].rs,val);
if(tr[tr[k].rs].prio>tr[k].prio)Zag(k);
}
Pushup(k);
}
void Delete(int&k,int val){
if(!k)return;
if(val==tr[k].val){
if(tr[k].cnt>1)tr[k].cnt--;
else if(tr[k].ls||tr[k].rs){
if(!tr[k].rs||tr[tr[k].ls].prio>tr[tr[k].rs].prio)
Zig(k),Delete(tr[k].rs,val);
else Zag(k),Delete(tr[k].ls,val);
}
else k=0;
Pushup(k);
return;
}
if(val<tr[k].val)Delete(tr[k].ls,val);
if(val>tr[k].val)Delete(tr[k].rs,val);
Pushup(k);
}
int Pre(int k,int val){
int res=-1;
while(k){
if(val>tr[k].val)res=tr[k].val,k=tr[k].rs;
else k=tr[k].ls;
}
return res;
}
int Nxt(int k,int val){
int res=-1;
while(k){
if(val<tr[k].val)res=tr[k].val,k=tr[k].ls;
else k=tr[k].rs;
}
return res;
}
}
using namespace Treap;
namespace SGT{
struct Data{int Sum,ls,rs;}tr[N*180];int tot;
void Modify(int&k,int l,int r,int x,int y,int t){
if(!k)k=++tot;
tr[k].Sum+=t*(y-x);
if(l==r)return;
if(x<=mid)Modify(tr[k].ls,l,mid,x,y,t);
if(mid<x)Modify(tr[k].rs,mid+1,r,x,y,t);
}
int Query(int k,int l,int r,int x,int y){
if(l>=x&&r<=y)return tr[k].Sum;
if(y<=mid)return Query(tr[k].ls,l,mid,x,y);
if(mid<x)return Query(tr[k].rs,mid+1,r,x,y);
return Query(tr[k].ls,l,mid,x,y)+Query(tr[k].rs,mid+1,r,x,y);
}
}
namespace Tree{
#define ls k<<1
#define rs k<<1|1
int rt[N<<2];
void Modify(int k,int l,int r,int x,int y,int t){
SGT::Modify(rt[k],1,n,y,x,t);
if(l==r)return;
if(x<=mid)Modify(ls,l,mid,x,y,t);
if(mid<x)Modify(rs,mid+1,r,x,y,t);
}
int Query(int k,int l,int r,int x,int y){
if(l>=x&&r<=y)return SGT::Query(rt[k],1,n,x,y);
if(y<=mid)return Query(ls,l,mid,x,y);
if(mid<x)return Query(rs,mid+1,r,x,y);
return Query(ls,l,mid,x,y)+Query(rs,mid+1,r,x,y);
}
}
signed main(){
n=read();m=read();
for(int i=1;i<=n;i++){
a[i]=read();
int pre=Pre(rt[a[i]],i);
Insert(rt[a[i]],i);
if(~pre)Tree::Modify(1,1,n,i,pre,1);
}
while(m--){
int opt=read(),x=read(),y=read(),pre,nxt;
if(opt==1){
Delete(rt[a[x]],x);
pre=Pre(rt[a[x]],x);nxt=Nxt(rt[a[x]],x);
if(~pre)Tree::Modify(1,1,n,x,pre,-1);
if(~nxt){
Tree::Modify(1,1,n,nxt,x,-1);
if(~pre)Tree::Modify(1,1,n,nxt,pre,1);
}
a[x]=y;
pre=Pre(rt[a[x]],x);nxt=Nxt(rt[a[x]],x);
if(~nxt){
if(~pre)Tree::Modify(1,1,n,nxt,pre,-1);
Tree::Modify(1,1,n,nxt,x,1);
}
if(~pre)Tree::Modify(1,1,n,x,pre,1);
Insert(rt[a[x]],x);
}
if(opt==2)printf("%lld\n",Tree::Query(1,1,n,x,y));
}
return 0;
}