题目描述
墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会像你发布如下指令:
1、 Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔。
2、 R P Col 把第P支画笔替换为颜色Col。
为了满足墨墨的要求,你知道你需要干什么了吗?
范围N,M <=10000 每个数都<=10^6
刚学会莫队就说说莫队:一种离线处理询问区间的好办法,虽然看上去超级暴力,但是很快。
把询问按左右端点排序,每次移动两个端点在O(1)的时间内更新答案即可。
while(L<q[i].l){/*O(1)更新答案*/L++;} while(L>q[i].l){L--;/*O(1)更新答案*/} while(R<q[i].r){R++;/*O(1)更新答案*/} while(R>q[i].r){/*O(1)更新答案*/R--;}
当然还要用到分块的优化。
P.S.排序以左端点所在的块为第一关键字,右端点为第二关键字
struct Node{ int l,r,id; bool operator <(const Node a){ if(belong[l]==belong[a.l])return r<a.r; else return belong[l]<belong[a.l]; } }q[MAXN];
感觉是不是暴力得让人难以接受=、= , 就是好用!
然而话说回来这个题带上了修改。
之前没有修改我们排序的是一个(l,r)
带了修改就不妨加上一维时间轴,排序这个(l,r,t)
再按照朴素的方法做,问题就迎刃而解了。
代码
#include<bits/stdc++.h> #define MAXN 10010 using namespace std; int N,M,Q,siz,tim,a[MAXN],belong[MAXN],ans[MAXN],color[100010],tmp; struct Node{ int l,r,id,t; bool operator <(const Node a){ if(belong[l]==belong[a.l]){ if(r==a.r)return t<a.t; else return r<a.r; } else return belong[l]<belong[a.l]; } }q[MAXN]; struct change{ int x,y; }c[MAXN]; void Change(int now,int i){ if(c[now].x>=q[i].l&&c[now].x<=q[i].r){ color[a[c[now].x]]--; if(color[a[c[now].x]]==0)tmp--; if(color[c[now].y]==0)tmp++; color[c[now].y]++; } swap(a[c[now].x],c[now].y); } int main() { scanf("%d%d",&N,&M); siz=sqrt(N); for(int i=1;i<=N;i++)scanf("%d",&a[i]),belong[i]=(i-1)/siz+1; for(int i=1;i<=M;i++){ int a,b;char s[5]; scanf("%s%d%d",s,&a,&b); if(s[0]=='Q') q[++Q]=(Node){a,b,Q,tim}; else c[++tim]=(change){a,b}; } sort(q+1,q+Q+1); int L=0,R=0,now=0; for(int i=1;i<=Q;i++){ while(L<q[i].l){color[a[L]]--;if(color[a[L]]==0)tmp--;L++;} while(L>q[i].l){L--;color[a[L]]++;if(color[a[L]]==1)tmp++;} while(R<q[i].r){R++;color[a[R]]++;if(color[a[R]]==1)tmp++;} while(R>q[i].r){color[a[R]]--;if(color[a[R]]==0)tmp--;R--;} while(now<q[i].t){now++;Change(now,i);} while(now>q[i].t){Change(now,i);now--;} ans[q[i].id]=tmp; } for(int i=1;i<=Q;i++) printf("%d ",ans[i]); return 0; }