【洛谷5251】[LnOI2019] 第二代图灵机(线段树+ODT)
點(diǎn)此看題面
大致題意: 有單點(diǎn)修改數(shù)字和區(qū)間著色兩種修改操作,詢問你某段區(qū)間內(nèi)包含所有顏色且數(shù)字和最小的子區(qū)間的數(shù)字和,或某段區(qū)間內(nèi)沒有重復(fù)顏色且數(shù)字和最大的子區(qū)間的數(shù)字和。數(shù)據(jù)隨機(jī)。
\(ODT\)維護(hù)顏色
看到區(qū)間著色且題目中強(qiáng)調(diào)數(shù)據(jù)隨機(jī),容易想到使用\(ODT\)去求解。
于是,我們就可以考慮用\(ODT\)來對(duì)顏色進(jìn)行維護(hù)。
線段樹維護(hù)數(shù)字和
但是考慮到要求區(qū)間數(shù)字和,\(ODT\)就很難搞了。
考慮到我們其實(shí)可以對(duì)于每個(gè)詢問,每次找到一個(gè)合法區(qū)間再詢問數(shù)字和更新答案。
也就是說,可以把維護(hù)顏色和維護(hù)數(shù)字和分開。
那維護(hù)數(shù)字和自然可以用線段樹嘍。
其實(shí)本來用樹狀數(shù)組似乎更優(yōu),但這題由于寫法問題可能還會(huì)需要求區(qū)間\(Min\)或\(Max\),樹狀數(shù)組搞起來就很吃力了。
處理詢問
以上大致介紹了,可以用線段樹維護(hù)數(shù)字和,\(ODT\)維護(hù)顏色。
接下來,再具體介紹一下如何去處理詢問。
考慮到這兩種詢問實(shí)際上都具有單調(diào)性,因此可以直接用雙指針搞。
我們枚舉右端點(diǎn),然后移動(dòng)左端點(diǎn)。
對(duì)于第一種詢問,我們需要在保證顏色數(shù)量等于\(c\)的情況下才能移動(dòng)左端點(diǎn),然后在移動(dòng)的同時(shí)更新答案(不然統(tǒng)計(jì)答案的過程會(huì)略顯麻煩)。
而\(c=1\)的情況可能要特判,直接輸出這段區(qū)間的最小值。
對(duì)于第二種詢問,我們只需在出現(xiàn)不合法情況,即出現(xiàn)重復(fù)顏色時(shí)移動(dòng)左端點(diǎn)。考慮我們此時(shí)剛把右端點(diǎn)的顏色加入,若出現(xiàn)重復(fù)顏色,必然是由右端點(diǎn)導(dǎo)致的,因此不斷移動(dòng)左端點(diǎn)直至右端點(diǎn)所屬顏色出現(xiàn)次數(shù)為\(1\)即可。
但注意,在處理第二種詢問時(shí),一旦出現(xiàn)右端點(diǎn)\(Size>1\)的情況,我們?cè)诮y(tǒng)計(jì)完其答案后,需將左端點(diǎn)移動(dòng)到右端點(diǎn)的位置上,不然就會(huì)出現(xiàn)重復(fù)顏色。
同理,在統(tǒng)計(jì)第二種詢問的答案時(shí),要求左端點(diǎn)的右邊界與右端點(diǎn)的左邊界之間的區(qū)間和,不然同樣會(huì)出現(xiàn)重復(fù)顏色。
而且,在第二種情況時(shí)容易漏考慮單個(gè)顏色的貢獻(xiàn),因此要初始化\(res\)為整段區(qū)間的\(Max\)。
大致就是這些吧。
代碼
#include<bits/stdc++.h> #define Tp template<typename Ty> #define Ts template<typename Ty,typename... Ar> #define Reg register #define RI Reg int #define Con const #define CI Con int& #define I inline #define W while #define N 100000 #define INF 1e9 #define Gmax(x,y) (x<(y)&&(x=(y))) #define Gmin(x,y) (x>(y)&&(x=(y))) using namespace std; int n,c,a[N+5]; class FastIO {private:#define FS 100000#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)#define pc(c) (C^FS?FO[C++]=c:(fwrite(FO,1,C,stdout),FO[(C=0)++]=c))#define tn (x<<3)+(x<<1)#define D isdigit(c=tc())int T,C;char c,*A,*B,FI[FS],FO[FS],S[FS];public:I FastIO() {A=B=FI;}Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);} Tp I void write(Ty x) {x<0&&(pc('-'),x=-x);W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}Tp I void writeln(Con Ty& x) {write(x),pc('\n');}I void clear() {fwrite(FO,1,C,stdout),C=0;} }F; class SegmentTree//線段樹 {private:#define STO l,hl,rt<<1#define ORZ hl+1,r,rt<<1|1#define PU(x) (O[x]=O[x<<1]+O[x<<1|1])int n,v[N+5];struct Interval//維護(hù)區(qū)間信息{int S,Mx,Mn;I Interval(CI s=0,CI mx=-INF,CI mn=INF):S(s),Mx(mx),Mn(mn){}I Interval operator + (Con Interval& t) Con {return Interval(S+t.S,max(Mx,t.Mx),min(Mn,t.Mn));}}O[N<<2];I void Build(CI l,CI r,CI rt)//建樹{if(!(l^r)) return (void)(O[rt]=Interval(v[l],v[l],v[l]));RI hl=l+r>>1;Build(STO),Build(ORZ),PU(rt);}I int qsu(CI l,CI r,CI rt,CI ql,CI qr)//詢問區(qū)間和{if(ql<=l&&r<=qr) return O[rt].S;RI hl=l+r>>1;return (ql<=hl?qsu(STO,ql,qr):0)+(qr>hl?qsu(ORZ,ql,qr):0);}I int qmx(CI l,CI r,CI rt,CI ql,CI qr)//詢問區(qū)間最大值{if(ql<=l&&r<=qr) return O[rt].Mx;RI hl=l+r>>1,t,res=-INF;return ql<=hl&&(t=qmx(STO,ql,qr),Gmax(res,t)),qr>hl&&(t=qmx(ORZ,ql,qr),Gmax(res,t)),res;}I int qmn(CI l,CI r,CI rt,CI ql,CI qr)//詢問區(qū)間最小值{if(ql<=l&&r<=qr) return O[rt].Mn;RI hl=l+r>>1,t,res=INF;return ql<=hl&&(t=qmn(STO,ql,qr),Gmin(res,t)),qr>hl&&(t=qmn(ORZ,ql,qr),Gmin(res,t)),res;}public:I void Init(CI x,int* s) {for(RI i=1;i<=x;++i) v[i]=s[i];Build(1,n=x,1);}I void Update(CI x,CI y)//單點(diǎn)修改(這里使用非遞歸寫法){RI l=1,r=n,rt=1,hl;W(l^r) hl=l+r>>1,x<=hl?(r=hl,rt<<=1):(l=hl+1,(rt<<=1)|=1);O[rt]=Interval(y,y,y);W(rt>>=1) PU(rt);}I int QSum(CI x,CI y) {return qsu(1,n,1,x,y);}I int QMax(CI x,CI y) {return qmx(1,n,1,x,y);}I int QMin(CI x,CI y) {return qmn(1,n,1,x,y);} }T; class ODT {private:#define IT set<Il>::iterator#define ins insert#define era erase#define fir first#define LB lower_bound#define add(x) (!cnt[x]++&&++tot)#define del(x) (!--cnt[x]&&--tot)int cnt[N+5];struct Il//維護(hù)區(qū)間信息{int l,r,v;I Il(CI x=0,CI y=0,CI p=0):l(x),r(y),v(p){}I bool operator < (Con Il& t) Con {return l<t.l;}};set<Il> S;I IT Sp(CI x)//分裂操作{IT t;if((t=S.LB(Il(x)))!=S.end()&&!(t->l^x)) return t;RI l=(--t)->l,r=t->r,v=t->v;S.era(t),S.ins(Il(l,x-1,v));return S.ins(Il(x,r,v)).fir;}public:I void Init(CI x,int* s)//初始化節(jié)點(diǎn)信息 {for(RI i=(s[0]=s[x+1]=-1,1),t=0;i<=x+2;++i) s[i]^s[i-1]&&(S.insert(Il(t,i-1,s[i-1])),t=i);}I void Assign(CI x,CI y,CI v)//推平操作{IT tr=Sp(y+1),tl=Sp(x);S.era(tl,tr),S.ins(Il(x,y,v));}I int Q1(CI x,CI y)//處理第一種詢問{if(!(c^1)) return T.QMin(x,y);//特判c=1memset(cnt,0,sizeof(cnt));IT tr=Sp(y+1),tl=Sp(x),ti=tl;RI t,res=INF,tot=0;//初始化W(ti!=tr) {add(ti->v);W(!(tot^c)) t=T.QSum(tl->r,ti->l),Gmin(res,t),del((tl++)->v);++ti;}//移動(dòng)右端點(diǎn),在保證顏色數(shù)量等于c的情況下才能移動(dòng)左端點(diǎn),然后在移動(dòng)的同時(shí)更新答案return res==INF?-1:res;//判斷是否無解,無解返回-1}I int Q2(CI x,CI y)//處理第二種詢問{memset(cnt,0,sizeof(cnt));IT tr=Sp(y+1),tl=Sp(x),ti=tl;RI t,res=T.QMax(x,y);//初始化W(ti!=tr){++cnt[ti->v];W(ti!=tl&&cnt[ti->v]>1) --cnt[(tl++)->v];//不斷移動(dòng)左端點(diǎn)直至右端點(diǎn)所屬顏色出現(xiàn)次數(shù)為1ti!=tl&&(t=T.QSum(tl->r,ti->l),Gmax(res,t));//更新答案if(ti->l^ti->r) W(ti!=tl) --cnt[(tl++)->v];++ti;//當(dāng)出現(xiàn)右端點(diǎn)Size>1的情況,將左端點(diǎn)移動(dòng)到右端點(diǎn)的位置上}return res;//返回答案} }O; int main() {RI Qtot,i,op,x,y,z;for(F.read(n,Qtot,c),i=1;i<=n;++i) F.read(a[i]);T.Init(n,a);//讀入數(shù)據(jù)for(i=1;i<=n;++i) F.read(a[i]);O.Init(n,a);W(Qtot--){switch(F.read(op,x,y),op)//處理操作{case 1:T.Update(x,y);break;case 2:F.read(z),O.Assign(x,y,z);break;case 3:F.writeln(O.Q1(x,y));break;case 4:F.writeln(O.Q2(x,y));break;}}return F.clear(),0; }轉(zhuǎn)載于:https://www.cnblogs.com/chenxiaoran666/p/Luogu5251.html
總結(jié)
以上是生活随笔為你收集整理的【洛谷5251】[LnOI2019] 第二代图灵机(线段树+ODT)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Logstash Introductio
- 下一篇: 洛谷 P2015 二叉苹果树