BZOJ2120&2453数颜色——线段树套平衡树(treap)+set/带修改莫队
题目描述
墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会像你发布如下指令: 1、 Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔。 2、 R P Col 把第P支画笔替换为颜色Col。为了满足墨墨的要求,你知道你需要干什么了吗?
输入
第1行两个整数N,M,分别代表初始画笔的数量以及墨墨会做的事情的个数。第2行N个整数,分别代表初始画笔排中第i支画笔的颜色。第3行到第2+M行,每行分别代表墨墨会做的一件事情,格式见题干部分。
输出
对于每一个Query的询问,你需要在对应的行中给出一个数字,代表第L支画笔到第R支画笔中共有几种不同颜色的画笔。
样例输入
6 5
1 2 3 4 5 5
Q 1 4
Q 2 6
R 1 2
Q 1 4
Q 2 6
1 2 3 4 5 5
Q 1 4
Q 2 6
R 1 2
Q 1 4
Q 2 6
样例输出
4
4
3
4
4
3
4
提示
对于100%的数据,N≤10000,M≤10000,修改操作不多于1000次,所有的输入数据中出现的所有整数均大于等于1且不超过10^6。
带修改莫队模板题
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<bitset>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
struct lty
{
int l,r,num,tim;
}q[10010];
struct miku
{
int x,y;
}a[10010];
int res;
int v[1000010];
int s[100010];
int ans[100010];
int num,cnt;
int n,m;
int block;
char ch[20];
bool cmp(lty a,lty b)
{
return (a.l/block==b.l/block)?(a.r==b.r?a.tim<b.tim:a.r<b.r):a.l<b.l;
}
void del(int x)
{
if(v[x]==1)
{
res--;
}
v[x]--;
}
void ins(int x)
{
if(!v[x])
{
res++;
}
v[x]++;
}
void change(int l,int r,int id)
{
if(a[id].x>=l&&a[id].x<=r)
{
if(--v[s[a[id].x]]==0)
{
res--;
}
if(++v[a[id].y]==1)
{
res++;
}
}
swap(a[id].y,s[a[id].x]);
}
int main()
{
scanf("%d%d",&n,&m);
block=pow(n,2/3);
for(int i=1;i<=n;i++)
{
scanf("%d",&s[i]);
}
for(int i=1;i<=m;i++)
{
scanf("%s",ch);
if(ch[0]=='Q')
{
cnt++;
scanf("%d%d",&q[cnt].l,&q[cnt].r);
q[cnt].num=cnt,q[cnt].tim=num;
}
else
{
num++;
scanf("%d%d",&a[num].x,&a[num].y);
}
}
sort(q+1,q+1+cnt,cmp);
int L=1,R=0,now=0;
for(int i=1;i<=cnt;i++)
{
while(L>q[i].l)
{
L--;
ins(s[L]);
}
while(R<q[i].r)
{
R++;
ins(s[R]);
}
while(L<q[i].l)
{
del(s[L]);
L++;
}
while(R>q[i].r)
{
del(s[R]);
R--;
}
while(now<q[i].tim)
{
now++;
change(q[i].l,q[i].r,now);
}
while(now>q[i].tim)
{
change(q[i].l,q[i].r,now);
now--;
}
ans[q[i].num]=res;
}
for(int i=1;i<=cnt;i++)
{
printf("%d\n",ans[i]);
}
}
对于每个点,记录这个点的颜色上一次出现的位置作为这个点的点权,每次查询l,r就相当于查询l到r之间点权小于l的点有多少个。实现起来直接用树套树就好了,外层用线段树维护序列区间信息,内层平衡树维护区间排名,每次只要找线段树对应点的平衡树内l的排名就好了。但修改比较麻烦,每次修改x点需要改变x修改前颜色中x的后继的前驱、x的前驱和修改后颜色中x的后继的前驱。对于每种颜色开一个set来维护前驱后继,每次修改时对应修改就好了,但要注意判断x是否有前驱和后继。
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int x,y;
int n,m;
int tot;
int opt;
char ch[3];
int a[50010];
int v[2000010];
int w[2000010];
int t[2000010];
int l[500010];
int r[500010];
int ls[2000010];
int rs[2000010];
int pre[50010];
int suf[50010];
int size[2000010];
int root[500010];
map<int,int>b;
set<int>s[1000010];
set<int>::iterator it;
int inbuild(int k)
{
tot++;
t[tot]=rand();
v[tot]=k;
w[tot]=1;
size[tot]=1;
ls[tot]=rs[tot]=0;
return tot;
}
void updata(int rt)
{
size[rt]=size[ls[rt]]+size[rs[rt]]+w[rt];
}
void lturn(int &rt)
{
int t=rs[rt];
rs[rt]=ls[t];
ls[t]=rt;
updata(rt);
updata(t);
rt=t;
}
void rturn(int &rt)
{
int t=ls[rt];
ls[rt]=rs[t];
rs[t]=rt;
updata(rt);
updata(t);
rt=t;
}
void insert(int &rt,int k)
{
if(!rt)
{
rt=inbuild(k);
return ;
}
if(v[rt]==k)
{
w[rt]++;
}
else
{
if(k<v[rt])
{
insert(ls[rt],k);
if(t[ls[rt]]<t[rt])
{
rturn(rt);
}
}
else
{
insert(rs[rt],k);
if(t[rs[rt]]<t[rt])
{
lturn(rt);
}
}
}
updata(rt);
}
void del(int &rt,int k)
{
if(v[rt]<k)
{
del(rs[rt],k);
}
else if(v[rt]>k)
{
del(ls[rt],k);
}
else
{
if(w[rt]>1)
{
w[rt]--;
}
else
{
if(!ls[rt]||!rs[rt])
{
rt=ls[rt]+rs[rt];
}
else
{
if(t[ls[rt]]<t[rs[rt]])
{
rturn(rt);
del(rs[rt],k);
}
else
{
lturn(rt);
del(ls[rt],k);
}
}
}
}
if(rt)
{
updata(rt);
}
}
int inrank(int rt,int k)
{
if(!rt)
{
return 0;
}
if(v[rt]==k)
{
return size[ls[rt]];
}
else if(v[rt]<k)
{
return size[ls[rt]]+w[rt]+inrank(rs[rt],k);
}
else
{
return inrank(ls[rt],k);
}
}
void outbuild(int rt,int L,int R)
{
l[rt]=L;
r[rt]=R;
for(int i=L;i<=R;i++)
{
insert(root[rt],pre[i]);
}
if(L!=R)
{
int mid=(L+R)>>1;
outbuild(rt<<1,L,mid);
outbuild(rt<<1|1,mid+1,R);
}
}
void change(int rt,int x,int y)
{
del(root[rt],pre[x]);
insert(root[rt],y);
if(l[rt]!=r[rt])
{
int mid=(l[rt]+r[rt])>>1;
if(x<=mid)
{
change(rt<<1,x,y);
}
else
{
change(rt<<1|1,x,y);
}
}
}
int outrank(int rt,int L,int R,int k)
{
if(L<=l[rt]&&r[rt]<=R)
{
return inrank(root[rt],k);
}
int mid=(l[rt]+r[rt])>>1;
if(R<=mid)
{
return outrank(rt<<1,L,R,k);
}
else if(L>mid)
{
return outrank(rt<<1|1,L,R,k);
}
return outrank(rt<<1,L,R,k)+outrank(rt<<1|1,L,R,k);
}
int main()
{
srand(12378);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
pre[i]=b[a[i]];
suf[b[a[i]]]=i;
b[a[i]]=i;
s[a[i]].insert(i);
}
for(int i=1;i<=n;i++)
{
if(!suf[i])
{
suf[i]=n+1;
}
}
outbuild(1,1,n);
for(int i=1;i<=m;i++)
{
scanf("%s",ch);
if(ch[0]=='R')
{
scanf("%d%d",&x,&y);
if(a[x]==y)
{
continue;
}
if(pre[x])
{
suf[pre[x]]=suf[x];
}
if(suf[x]!=n+1)
{
change(1,suf[x],pre[x]);
pre[suf[x]]=pre[x];
}
s[a[x]].erase(x);
a[x]=y;
s[a[x]].insert(x);
it=s[a[x]].lower_bound(x);
if(it!=s[a[x]].begin())
{
it--;
suf[(*it)]=x;
change(1,x,(*it));
pre[x]=(*it);
it++;
}
else
{
change(1,x,0);
pre[x]=0;
}
if(++it!=s[a[x]].end())
{
suf[x]=(*it);
change(1,(*it),x);
pre[(*it)]=x;
}
else
{
suf[x]=n+1;
}
}
else
{
scanf("%d%d",&x,&y);
printf("%d\n",outrank(1,x,y,x));
}
}
}

浙公网安备 33010602011771号