1. 程式人生 > >洛谷P4344 腦洞治療儀 [SHOI2015] 線段樹+二分/珂朵莉樹(未完成QAQ)

洛谷P4344 腦洞治療儀 [SHOI2015] 線段樹+二分/珂朵莉樹(未完成QAQ)

正解:線段樹+二分/珂朵莉樹

解題報告:

先放個傳送門qwq(啊發現好多題解都忘了放傳送門呢QAQ有時間一起補上QAQ

然後大概說下思路,其實比較好想只是實現有點兒複雜呢?

大概就是個線段樹+二分鴨

首先建棵線段樹,存這段區間內所有1的個數

港最簡單的操作,就是那個0lr.相當於就lr都變成0,沒什麼可說的嗷,基本操作qwq

然後1和2的操作想法都是類似的,分別港下趴還是qwq

首先1l0r0l1r1,肯定可以直接賦值0了不解釋

首先如果l0r0中1的數量是大於​l0r0中0的數量就直接讓l1r1賦值為1就成了不需要別的操作了,依然基本操作呢

但是如果小於,思考怎麼做

肯定是從前往後跑能

補就補就是了,那怎麼看能不能補呢,首先我們肯定要先定位到第一個是0的點

這個可以用query直接求下判斷是否是1,然後如果是1就二分查詢找到第一個是0的點(關於二分的這個之後會另外講實現的qwq)

然後繼續用二分找到第一個不是0的點,那麼中間這一段就都是0

然後判斷,如果這一段的0的長度大於可以補的長度了,直接把能補的長度那段賦值成1,然後就補不上了退出

然後如果小於等於,就可以繼續補,就把這一段賦值成1繼續補重複之間的步驟就成

然後操作1就講完了,還是比較好理解的呢?

然後是2lr,這個一樣是用的二分(聽說,還有種神仙方法線段樹存的是最長0序列長度?這個怎麼操作?mk下會學習落實的qwq)

差不多套路,首先判斷是不是1如果是1讓它跳到0去

然後二分查詢找出這一段0的長度,然後ans取長度max就成

最後說下這個二分,就可以結束啦!

首先明確mid指這一段中連續的0/1(函式中用k表示)的長度

然後query查詢,查這一段的和是多少,如果和=k*這段長度,說明這是一段連續長度,說明能繼續延伸,就l=mid+1

否則不能的話就r=mid-1咯

然後就完美結束啦?那這道題就還有倆坑沒填,一個是線段樹存最長做法,一個是珂朵莉做法

沒啦!

 

#include<bits/stdc++.h>
using
namespace std; #define ll int #define rp(i,x,y) for(register ll i=x;i<=y;++i) #define lc(x) x<<1 #define rc(x) (x<<1)|1 #define md(x,y) (x+y)>>1 const ll N=200000+10; ll n,m,tr[N<<2],add[N<<2],trtr[N<<2]; inline ll read() { char ch=getchar();ll x=0;bool y=1; while(ch!='-' && (ch<'0' || ch>'9'))ch=getchar(); if(ch=='-')y=0,ch=getchar(); while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=getchar(); return y?x:-x; } inline void pushdown(ll d,ll l,ll r,ll mid) { if(add[d]!=0) { --add[d]; tr[lc(d)]=add[d]*(mid-l+1);tr[rc(d)]=add[d]*(r-mid); add[lc(d)]=add[d]+1;add[rc(d)]=add[d]+1; add[d]=0; } } void update(ll d,ll l,ll r,ll x,ll y,ll k) { if(l>y || x>r)return; if(l>=x && r<=y){tr[d]=(r-l+1)*k;add[d]=k+1;return;} ll mid=md(l,r);pushdown(d,l,r,mid); if(x<=mid)update(lc(d),l,mid,x,y,k); if(mid<y)update(rc(d),mid+1,r,x,y,k); tr[d]=tr[lc(d)]+tr[rc(d)]; } void build(ll d,ll l,ll r) { if(l==r){tr[d]=1;return;} ll mid=md(l,r); build(lc(d),l,mid);build(rc(d),mid+1,r); tr[d]=tr[lc(d)]+tr[rc(d)]; } ll query(ll d,ll l,ll r,ll x,ll y) { if(l>y||x>r)return 0;if(x<=l&&r<=y)return tr[d]; ll mid=md(l,r),ret=0; pushdown(d,l,r,mid); if(mid>=x)ret+=query(lc(d),l,mid,x,y); if(mid<y)ret+=query(rc(d),mid+1,r,x,y); return ret; } inline ll efcz(ll x,ll y,ll k) { if (x>y) return -1; ll l=0,r=y-x,mid; while (l<=r) { mid=md(l,r); if(query(1,1,n,x,x+mid)==k*(mid+1))l=mid+1; else r=mid-1; } return l; } inline void work1(){ll t1=read(),t2=read();update(1,1,n,t1,t2,0);return;} inline void work2() { ll t1=read(),t2=read(),t3=read(),t4=read(); int num=query(1,1,n,t1,t2),p=0; update(1,1,n,t1,t2,0); if (num>=t4-t3+1-query(1,1,n,t3,t4))update(1,1,n,t3,t4,1); else { if(num!=0) { while(1) { if (query(1,1,n,t3,t3)==1){p=efcz(t3,t4,1);t3+=p;} p=efcz(t3,t4,0); if(p==-1)break; if (p>num){update(1,1,n,t3,t3+num-1,1);break;} update(1,1,n,t3,t3+p-1,1);num-=p;t3+=p; } } } } inline void work3() { ll t1=read(),t2=read(); ll ans=0,p=0; while(1) { if (query(1,1,n,t1,t1)==1){p=efcz(t1,t2,1);t1+=p;} p=efcz(t1,t2,0); if(p==-1)break; ans=max(ans,p);t1+=p; if(t1>t2)break; } printf("%lld\n",ans); } int main() { n=read();m=read();build(1,1,n); while(m--){ll t=read();if(t==0)work1();if(t==1)work2();if(t==2)work3();} return 0; }
想起來還要放個程式碼呢QAQ