題解 洛谷P1903/BZOJ2120【[國家集訓隊]數顏色 / 維護隊列】
阿新 • • 發佈:2019-01-15
efi zoj return esp fin while 結構體 line 參考
提交上去,哇,~
~
對於不會樹套樹、主席樹的本蒟蒻,還是老老實實的用莫隊做吧....
其實這題跟普通莫隊差不了多遠,無非就是有了一個時間,當我們按正常流程排完序後,按照基本的莫隊來,做莫隊時每次循環對於這一次操作,我們在結構體中記錄一下這次操作前有多少個改值操作,然後將當前的ans和每個點的顏色信息更新至當前隊列(此隊列不是莫隊,是原隊列)的狀態再移動l,r兩指針。
簡單點說:
設當前詢問為a,下一個詢問為b,我們已知a,要求b。
首先我們像普通莫隊一樣轉移左右端點。
這時候我們可能會發現a和b的經歷的修改次數不同!
假如a較b少修改了p次,那我們就把這p次修改一個一個從前往後暴力地加上去。假如a較b多修改了q次,那我們就把這q次修改從後往前還原掉。
還要註意一點:
帶修改的莫隊的詢問排序方法為:
- 第一關鍵字:左端點所在塊編號,從小到大排序。
- 第二關鍵字:右端點所在塊編號,從小到大排序。
- 第三關鍵字:經歷的修改次數。也可以說是詢問的先後,先詢問的排前面。
註意的幾點:
- 時間上的優化:將塊的大小從sqrt(n)改為n的二分之三次方
- 可修改莫隊只用於單點修改,區間修改的題目就算了吧...
- 復雜度大約為O(n的五分之三次方)
- 一樣是離線操作
但是具體怎麽操作呢?
我們先看一下結構體:
struct Node{ int l,r,c,id; bool operator < (const Node a)const { if(l/Be==a.l/Be){ if(r/Be==a.r/Be)return id<a.id; return r<a.r; }return l<a.l; } }q[N];
上面的重載運算符就是按照上文的排序方式來寫的。
結構體中l,r分別是詢問的左右端點,c是該詢問之前的修改操作次數,id是這個操作的位置。
再來比較一下普通的莫隊操作和可修改的莫隊操作:
更新ans的函數(一樣的):
inline void Add(int x){if(!sum[x])ans++;sum[x]++;}
inline void Del(int x){sum[x]--;if(!sum[x])ans--;}
普通莫隊:
for(register int i=1;i<=m;++i){ while(l>q[i].l)l--,Add(val[l]); while(r<q[i].r)r++,Add(val[r]); while(l<q[i].l)Add(val[l]),l++; while(r>q[i].r)Add(val[r]);,r--; Ans[q[i].id]=ans; }
可修改莫隊:
for(register int i=0;i<c1;++i){
for(;lst<q[i].c;lst++){
if(l<=Q[lst][0]&&Q[lst][0]<=r)
Del(Q[lst][1]),Add(Q[lst][2]);
val[Q[lst][0]]=Q[lst][2];
}
for(;lst>q[i].c;lst--){
if(l<=Q[lst-1][0]&&Q[lst-1][0]<=r)
Del(Q[lst-1][2]),Add(Q[lst-1][1]);
val[Q[lst-1][0]]=Q[lst-1][1];
}
for(++r;r<=q[i].r;r++)Add(val[r]);
for(--r;r>q[i].r;r--)Del(val[r]);
for(--l;l>=q[i].l;l--)Add(val[l]);
for(++l;l<q[i].l;l++)Del(val[l]);
Ans[q[i].id]=ans;
}
(註:可修改莫隊的下面四個for循環,是在我的心態極不好的時候才從讓我WA了幾遍的while循環改過來的,但是後面才發現不是while的原因......貼代碼過來時不想改了)
我們發現,可修改莫隊多了一段:
for(;lst<q[i].c;lst++){
if(l<=Q[lst][0]&&Q[lst][0]<=r)
Del(Q[lst][1]),Add(Q[lst][2]);
val[Q[lst][0]]=Q[lst][2];
}
for(;lst>q[i].c;lst--){
if(l<=Q[lst-1][0]&&Q[lst-1][0]<=r)
Del(Q[lst-1][2]),Add(Q[lst-1][1]);
val[Q[lst-1][0]]=Q[lst-1][1];
}
沒錯!這就是這段話:
假如a較b少修改了p次,那我們就把這p次修改一個一個從前往後暴力地加上去。假如a較b多修改了q次,那我們就把這q次修改從後往前還原掉。
上面的if是來判斷這個改色操作是否在當前莫隊範圍中,在的話肯定要維護一下當前的莫隊ans值嘛,但是不在範圍中的話,直接改顏色不就好了......(貌似並沒有那麽難操作誒)
輸入時的操作:
用一個變量c1表示有多少個詢問操作,c2表示有多少個更改操作。
- 用結構體保存每次詢問操作的相關信息
- 用一個二維數組保存每次更改操作的相關信息
代碼:
for(int i=1,a,b;i<=m;i++)
if(scanf("%s",opt),read(a),read(b),opt[0]==‘Q‘)
q[c1].l=a,q[c1].r=b,q[c1].id=c1,q[c1].c=c2,c1++;
else Q[c2][0]=a,Q[c2][1]=C[a],Q[c2][2]=C[a]=b,c2++;
- Q[i][0] 表示第i次操作需更改的位置
- Q[i][1] 表示第i次操作更改位置的現在顏色
- Q[i][2] 表示第i次操作要改成的顏色
- 註意,Q[i][1]在之前的更改操作中可能已經被改過,而我們有要記錄這個點現在的值,所以我維護了一個C數組來隨時記錄每次操作的變化,原序列數組為val數組,顯然是不能用原序列數組來記錄的(不然到莫隊時就亂了)
說了這麽多,總算是要貼AC代碼了。
AC代碼(請原諒我鬼畜的碼風,壓行壓瘋了...):
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define A printf("A")
#define P(x) printf("V %d V",x);
#define S 1000003
using namespace std;
const int N=5e4+5;
template<typename _Tp>inline void read(_Tp&dig){
char c;dig=0;
while(c=getchar(),!isdigit(c));
while(isdigit(c))dig=dig*10+c-‘0‘,c=getchar();
}
int n,m,Be,c1,c2,ans,C[N],val[N],Ans[N],sum[S],Q[N][3];
struct Node{
int l,r,c,id;
bool operator < (const Node a)const {
if(l/Be==a.l/Be){
if(r/Be==a.r/Be)return id<a.id;
return r<a.r;
}return l<a.l;
}
}q[N];char opt[10];
inline void Add(int x){if(!sum[x])ans++;sum[x]++;}
inline void Del(int x){sum[x]--;if(!sum[x])ans--;}
int main(){
read(n);read(m);Be=pow(n,(double)2/(double)3);
for(register int i=1;i<=n;++i)read(val[i]),C[i]=val[i];
for(int i=1,a,b;i<=m;i++)
if(scanf("%s",opt),read(a),read(b),opt[0]==‘Q‘)
q[c1].l=a,q[c1].r=b,q[c1].id=c1,q[c1].c=c2,c1++;
else Q[c2][0]=a,Q[c2][1]=C[a],Q[c2][2]=C[a]=b,c2++;
sort(q,q+c1),Add(val[1]);int l=1,r=1,lst=0;
for(register int i=0;i<c1;++i){
for(;lst<q[i].c;lst++){
if(l<=Q[lst][0]&&Q[lst][0]<=r)
Del(Q[lst][1]),Add(Q[lst][2]);
val[Q[lst][0]]=Q[lst][2];
}
for(;lst>q[i].c;lst--){
if(l<=Q[lst-1][0]&&Q[lst-1][0]<=r)
Del(Q[lst-1][2]),Add(Q[lst-1][1]);
val[Q[lst-1][0]]=Q[lst-1][1];
}
for(++r;r<=q[i].r;r++)Add(val[r]);
for(--r;r>q[i].r;r--)Del(val[r]);
for(--l;l>=q[i].l;l--)Add(val[l]);
for(++l;l<q[i].l;l++)Del(val[l]);
Ans[q[i].id]=ans;
}for(register int i=0;i<c1;++i)printf("%d\n",Ans[i]);
return 0;
}
提交上去,哇,~跑的真快~慢死了!(好吧,我覺的還需優化一下......)
** 參考隔壁機房 叉ZY 大佬的文章,在此鳴謝叉ZY大佬
~我居然因為少了個等於號調了一個多小時.....~
題解 洛谷P1903/BZOJ2120【[國家集訓隊]數顏色 / 維護隊列】