bzoj 2120 数颜色 (带修莫队)
题目链接:
https://www.lydsy.com/JudgeOnline/problem.php?id=2120
题意:两种操作:Q 询问区间 l - r 内颜色的种类 ,R 单点修改
思路:
带修莫队与普通莫队不同之处就是,带修莫队可以支持修改操作,我们可以再维护一维来表示操作的时间,那么离线处理询问时,我们就需要维护 l,r,t,三根指针,同时因为是三根指针
块的大小分成 n的2/3次方,其他地方和普通莫队维护是一样的,只是多维护了一维操作时间,可能看上去会绕一点。
实现代码:
#include<bits/stdc++.h> using namespace std; const int M = 1e4 + 10; struct node{ int l,r,t,id; node(int l=0,int r=0,int t=0,int id=0):l(l),r(r),t(t),id(id){} }q[M]; struct node1{ int pos,now,old; node1(int pos=0,int now=0,int old=0):pos(pos),now(now),old(old){} }c[M]; int n,m,block,l,r,num[M],a[M],now[M],flag[M*100],ans; //排序优先度如果l,r都在一个块中,那么优先选择t小的 bool cmp(node a,node b){ if(a.l/block != b.l/block){ if(a.r/block != b.r/block){ return a.t < b.t; } return a.r < b.r; } return a.l < b.l; } void add(int col,int val){ flag[col] += val; if(val > 0) ans += (flag[col] == 1); else if(val < 0) ans -= (flag[col]==0); } void solve(int pos,int col){ if(pos >= l&&pos <= r) add(col,1),add(a[pos],-1); a[pos] = col; } int main() { scanf("%d%d",&n,&m); block = (int)pow(n,2.0/3.0); for(int i = 1;i <= n;i ++){ scanf("%d",&a[i]); now[i] = a[i]; //now[i] i点现在的颜色 } int k = 0; int cnt = 0; ans = 0; for(int i = 0;i < m;i ++){ char op[2]; int x,y; scanf("%s%d%d",&op,&x,&y); if(op[0] == 'Q'){ //将询问的区间左右节点,在第k次修改之后,第cnt个询问等信息存到结构体q中 q[++cnt] = node(x,y,k,cnt); } else { //将第k次修改的点的左边,要修改的颜色,这个点之前的颜色,存到结构体里 c[++k] = node1(x,y,now[x]); now[x] = y; //x点现在的颜色变为y } } sort(q+1,q+cnt+1,cmp); l = 1; r = 0; int tim = 0; for(int i = 1;i <= cnt;i ++){ while(tim < q[i].t) solve(c[tim+1].pos,c[tim+1].now),tim++; while(tim > q[i].t) solve(c[tim].pos,c[tim].old),tim--; while(q[i].l < l) add(a[l-1],1),l--; while(q[i].l > l) add(a[l],-1),l++; while(q[i].r < r) add(a[r],-1),r--; while(q[i].r > r) add(a[r+1],1),r++; num[q[i].id] = ans; } for(int i = 1;i <= cnt;i ++) printf("%d\n",num[i]); return 0; }