題解 SP30906 【ADAUNIQ - Ada and Unique Vegetable】
阿新 • • 發佈:2021-10-25
\[\text{題目大意}
\]
\(\quad\)你需要維護一個長度為 \(n\) 的序列,支援兩種操作。
- 第一種操作為 \(1\) \(x\) \(y\),代表將第 \(x\) 個數修改為 \(y\)。
- 第二種操作為 \(2\) \(l\) \(r\),需要你求出在第 \(l\) 到 \(r\) 個數中恰好出現了一次的數的數量。
- \(n,a_i≤2 \times10^5\)。
- 本題的下標從 \(0\) 開始。(WA了一次就是這個原因)
\(\quad\)本題就是帶修莫隊板子題,沒有學過的可以先做做P1903 [國家集訓隊]數顏色 / 維護佇列。
\(\quad\)大體思路就是先儲存詢問和修改操作,分開來存,並記錄時間,之後將詢問排序。
- 以詢問左邊界 \(l\) 為第一關鍵字,按塊的序號排序。
- 以詢問右邊界 \(r\) 為第二關鍵字,按塊的序號排序。
- 以時間 \(t\) 為第三關鍵字,按先後排序。
\(\quad\)之後依次處理詢問,記錄答案即可。
\(\quad\)值得一提的是塊的大小一般是 \(n^{ \frac{2}{3}}\),這樣時間複雜度理論上最簡,為 。
\(\quad\)還有什麼不懂的就看看程式碼吧!
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #define re register int #define il inline using namespace std; il int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)&&ch!='-')ch=getchar(); if(ch=='-')f=-1,ch=getchar(); while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); return x*f; } il void print(int x) { if(x<0)putchar('-'),x=-x; if(x/10)print(x/10); putchar(x%10+'0'); } const int N=2e5+5; int n,m,blo,num,t1,t2,a[N],p[N],ans[N]; struct node{ int l,r,id,tim; }q[N]; struct nod{ int id,c1,c2; }c[N]; il bool cmp(node a,node b){ if(a.l/blo!=b.l/blo)return a.l/blo<b.l/blo;//以詢問左邊界l為第一關鍵字,按塊的序號排序。 if(a.r/blo!=b.r/blo)return a.r/blo<b.r/blo;//以詢問右邊界r為第二關鍵字,按塊的序號排序。 return a.tim<b.tim; //以時間t為第三關鍵字,按先後排序。 } il void add(int x) { if(p[x]==1)num--; p[x]++; if(p[x]==1)num++; } il void del(int x) { if(p[x]==1)num--; p[x]--; if(p[x]==1)num++; } signed main() { n=read();m=read();blo=pow(n,0.666);//blo表示塊長 for(re i=1;i<=n;i++)a[i]=read(); for(re i=1;i<=m;i++) { int op=read(); if(op==2){ q[++t1].l=read()+1;q[t1].r=read()+1;//題目所給的下標從0開始 q[t1].id=t1;q[t1].tim=t2;//id用來儲存答案,tim為時間軸 } else { c[++t2].id=read()+1;//題目所給的下標從0開始,id記錄修改的位置 c[t2].c1=a[c[t2].id];//記錄之前的數字,方便撤銷 a[c[t2].id]=c[t2].c2=read();//修改操作 } } sort(q+1,q+t1+1,cmp); int l=1,r=0,t=t2;//因為前面輸入的時候把修改操作都做了一遍,所以此時時間t=t2 for(re i=1;i<=t1;i++) { //依次調整l,r while(l<q[i].l)del(a[l++]); while(l>q[i].l)add(a[--l]); while(r<q[i].r)add(a[++r]); while(r>q[i].r)del(a[r--]); while(t<q[i].tim){//修改 int x=c[++t].id; if(x>=l&&x<=r)del(a[x]);//只有x符合範圍才有可能修改答案 a[x]=c[t].c2; if(x>=l&&x<=r)add(a[x]); } while(t>q[i].tim){//撤銷 int x=c[t].id; if(x>=l&&x<=r)del(a[x]); a[x]=c[t--].c1; if(x>=l&&x<=r)add(a[x]); } ans[q[i].id]=num; } for(re i=1;i<=t1;i++)print(ans[i]),putchar('\n'); return 0; }