题目描述
墨墨购买了一套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 ",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 ",outrank(1,x,y,x)); } } }