前言
今天考场上忘了,只记得使用主席树.所以特意来补一篇.
静态
不带修改,进行查询.
如果只是单纯的对每一个位置建一颗主席树,在查询时进行减法的话,显然无法做到直接出结果。为此,我们可以在任意一棵主席树上规定[l,r]的值是这一段区间内有多少种不同的颜色。如果
并且
的颜色相同,那我们规定pos1处没有颜色,pos2处有颜色.永远保证更靠近r的那一边有颜色.这样区间查询时,查以r为根的树,并且始终保证位置在l的右边。
对于每一个颜色记录一个前驱.这样就能够删除自己之前出现的点的贡献.
code
#include<bits/stdc++.h>
#define _ 30011
#define LL long long
using namespace std;
int rt[_],c[_*20][2],val[_*40],last[1000010];
inline char gc(){
static char buf[1<<13],*p1=buf,*p2=buf;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,1<<13,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){
register int data=0,w=1;
char ch=0;
while(ch<'0'||ch>'9'){ch=gc();if(ch == '-')w=-1;}
while(ch<='9'&&ch>='0'){
data=(data<<3)+(data<<1)+(ch^48);
ch=gc();
}
return w*data;
}
int s[_],cnt;
int n,q,limit,reff[_];
inline void update(int T){
val[T]=val[c[T][1]]+val[c[T][0]];
return;
}
inline void build(int pre,int &T,int l,int r,int pos,int w){
T=++cnt;
if(l==r){
val[T]=val[pre]+w;
return;
}
register int mid = (l+r)>>1;
if(pos<=mid){
c[T][1]=c[pre][1];
build(c[pre][0],c[T][0],l,mid,pos,w);
}
else {
c[T][0]=c[pre][0];
build(c[pre][1],c[T][1],mid+1,r,pos,w);
}
update(T);
return;
}
void fix(register int T,register int l,register int r,register int pos){
if(l==r){
val[T]-=1;
return;
}
register int mid=(l+r)>>1;
if(pos<=mid)fix(c[T][0],l,mid,pos);
else fix(c[T][1],mid+1,r,pos);
update(T);
return;
}
inline int query(int T,register int l,register int r,register int pos){
if(l==r)return val[T];
register int mid = (l+r)>>1;
if(mid>=pos)return val[c[T][1]]+query(c[T][0],l,mid,pos);
else return query(c[T][1],mid+1,r,pos);
/*if(pos<mid+1)return val[c[T][1]]+query(c[T][0],l,mid,pos);
return query(c[T][1],mid+1,r,pos);*/
}
void yi(int &T,int l,int r){
T=++cnt;
if(l==r)return;
register int mid = (l+r)>>1;
yi(c[T][0],l,mid);
yi(c[T][1],mid+1,r);
return;
}
void dfs(int T,int l,int r){
if(!T)return;
register int mid = (l+r)>>1;
cout<<val[T]<<' '<<l<<' '<<r<<endl;
dfs(c[T][0],l,mid);
dfs(c[T][1],mid+1,r);
}
int main(){
n=read();
for(register int i=1;i<=n;++i)s[i]=read(),limit=max(limit,s[i]);
yi(rt[0],1,n);
for(register int i=1;i<=n;++i){
if(last[s[i]]){
//cout<<s[i]<<' '<<last[s[i]]<<endl;
register int gg;
build(rt[i-1],gg,1,n,i,1);
build(gg,rt[i],1,n,last[s[i]],-1);
}
else {
build(rt[i-1],rt[i],1,n,i,1);
}
last[s[i]]=i;
}
//dfs(rt[2],1,n);//return 0;
q=read();
register int L,R;
for(register int i=1;i<=q;++i){
L=read(),R=read();
printf("%d\n",query(rt[R],1,n,L));
}
}
动态
但动态怎么处理?最容易的的思路是修改树上的信息。当题目中出现把某处位置的颜色更改时,需要删除改点原来颜色的贡献,新增现在颜色的贡献.如果该位置是
,那么
的位置都要修改。这个时候就要使用树套树。外层的树状数组支持快速修改.如果就在刚才静态的上面修改一下代码,最大的困难点在于询问。树状数组套用下的区间,维护的是一段区间的情况。
所以要彻底更换维护方式.
首先主席树不能用了.(树套树很少有主席树).采用树状数组套线段树.,每一个位置开一颗,每一棵线段树的意义是,其维护所有颜色的前驱的位置在其前面的信息.很显然,如果一段区间l,r想要询问的话,其一定会去l的线段树内找,因为存在l-r重复的话重复的那一个的前驱会在l-r中,l维护的是前驱小于l的.
现在考虑修改。这个时候就要用splay(建议 set)。分为删除与插入.
删除
- 这个点的前驱那里保留了他的信息,需要删掉。
- 他曾经作为别人的前驱保留过别人的前驱,删掉。
- 新加入的颜色在他的前驱那删掉他的后继的信息.
加入
- 这个点的后继要把他的信息加入到这个点的前驱.
- 新加入的颜色,在他的前驱那加入自己的信息.
- 新加入的颜色,作为他的后继的新前驱加入信息.
查询
树状数组上查询即可.
code
// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<set>
#include<algorithm>
#define lowbit(k) (k&(-k))
using namespace std;
const int _ (10010);
const int __ (1e6+1e2);
int rt[_],c[_*150][2],val[_*150],cnt;//树的基本
set <int> s[__];//颜色基本;
set <int> ::iterator pre_it;//迭代器基本
set <int >::iterator it;
set <int >::iterator las_it;
int N,M,col[_];
void fix(int &T,int l,int r,const int loc,const int w){
if(!T)T=++cnt;
val[T]+=w;
if(l==r)return;
register int mid=(l+r)>>1;
loc<=mid?fix(c[T][0],l,mid,loc,w):fix(c[T][1],mid+1,r,loc,w);
return;
}
inline void pre_fix(register int k,register int w,register int loc){
for(++k;k<=N;k+=lowbit(k))fix(rt[k],1,N,loc,w);
return;
}
int query(int T,const int ql,const int qr,int l,int r){
if(!T)return 0;
if(l==r)return val[T];
if(ql<=l&&qr>=r)return val[T];
register int mid = (l+r)>>1,res=0;
if(ql<=mid)res+=query(c[T][0],ql,qr,l,mid);
if(qr>mid)res+=query(c[T][1],ql,qr,mid+1,r);
return res;
}
inline int pre_query(register int l,register int r){
register int p=l,res=0;
for(;p;p-=lowbit(p))res+=query(rt[p],l,r,1,N);
return res;
}
set <int >::iterator operator - (set<int >::iterator a,int b){
set <int >::iterator d=a;
for(register int i=1;i<=b;++i)d--;
return d;
}
int main(){
std::ios::sync_with_stdio(false);
cin>>N>>M;
register int i,j,l,r,pre_col,now_col;
register char ch;
for(i=1;i<=N;++i){
cin>>col[i];
s[col[i]].empty()?pre_fix(0,1,i):pre_fix(*(s[col[i]].end()-1),1,i);
s[col[i]].insert(i);
}
for(i=1;i<=M;++i){
cin>>ch;
if(ch=='Q'){
cin>>l>>r;
printf("%d\n",pre_query(l,r));
}
else {
cin>>l>>now_col;//注:把第l只笔换成r颜色
if(col[l]==now_col)continue;
pre_col=col[l];
col[l]=now_col;
it=s[pre_col].find(l);
pre_it=it,--pre_it;
las_it=it,++las_it;
it==s[pre_col].begin()?pre_fix(0,-1,l):pre_fix(*pre_it,-1,l);
//if(i==8)return 0;
if(it!=(s[pre_col].end()-1)){
pre_fix(*it,-1,*las_it);
it==s[pre_col].begin()?pre_fix(0,1,*las_it):pre_fix(*pre_it,1,*las_it);
}
s[pre_col].erase(l);
//以上是删除
s[now_col].insert(l);
it=s[now_col].find(l);
pre_it=it,--pre_it;
las_it=it,++las_it;
if(it==s[now_col].begin()&&it==(s[now_col].end()-1)){
pre_fix(0,1,*it);
continue;
}
if(it==s[now_col].begin()){
pre_fix(0,-1,*las_it);
pre_fix(0,1,*it);
pre_fix(*it,1,*las_it);
continue;
}
if(it==(s[now_col].end()-1)){
pre_fix(*pre_it,1,*it);
continue;
}
pre_fix(*pre_it,-1,*las_it);
pre_fix(*pre_it,1,*it);
pre_fix(*it,1,*las_it);
}
}
}