HDU - 3667 Hotel(线段树+区间合并)
題目鏈接:點擊查看
題目大意:給出n個連續的空房間,依次進行m個操作,操作一是查詢操作,查詢在總區間內的一段連續的長度為x的空房間,并
且該位置要靠左,如果查詢到返回最左邊的端點,并將其占用,找不到返回0。操作二是更新操作,更新以x為起點,長度為len的
房間為空房間
題目分析:這個題目雖然明白是要使用線段樹,但是卻無從下手,還是沒辦法將題目抽象為模型來想,還需要多加訓練才行啊!
在找題解的時候看到一個大佬的感悟我覺得很贊同:做線段樹的題目不能為了做題而做題,線段樹是一種數據結構,是為了解決
問題而產生的,所以具體問題需要具體分析,因此在做題的時候,需要分析題目的本質,然后在思考該怎么使用線段樹去解決問
題,而不是單純的就這題解出發,如果時間長了,會陷入思維定勢,遇到變形的題目反而一點頭緒都沒有。
回到這個題目,這個題目的查詢操作和更新操作都可以對應著線段樹的區間查詢和區間更新來完成,但這個題的一個難點是如查
找最左端的符合條件的區間。
那么這里就針對于查找的函數來詳細解釋一下吧,目前做線段樹和區間合并的問題的時候,感覺難點并不是pushup函數,反而是
查找函數,一般的變化都出在這上面。
對于本題的query函數,我們因為需要查找最左端滿足條件的區間,所以我們現在先枚舉出三個滿足條件的區間:
設k為當前節點,tree為線段樹的數組,all為區間中最長的連續空房間數量,rr為區間中從右端點開始的最長連續空房間數量,
ll為區間中從左端點開始的最長連續空房間數量
那么假設我們將每個節點都分為左右兩個節點:
按照從一到二到三枚舉條件,若滿足向下遞歸,若不滿足返回該返回的值,若三個條件都不滿足則返回0,可以設置剪枝,即當
這個區間的最大長度為0的時候可以直接返回,因為接下來的這三個判斷都毫無意義了。
上代碼把,我自己寫的代碼很亂,然后看到網上大佬的博客后適當封裝了一下,稍微能好看一點,都掛上吧:
蒟蒻的代碼:
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<algorithm> #include<stack> #include<queue> #include<map> #include<sstream> #include<cmath> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=5e4+100;struct Node {int l,r;int ll,rr,all;int lazy; }tree[N<<2];void build(int k,int l,int r) {tree[k].l=l;tree[k].r=r;tree[k].all=tree[k].ll=tree[k].rr=r-l+1;tree[k].lazy=-1;if(l==r)return;int mid=(l+r)>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r); }void pushup(int k) {tree[k].ll=tree[k<<1].ll;if(tree[k].ll==tree[k<<1].r-tree[k<<1].l+1)tree[k].ll+=tree[k<<1|1].ll;tree[k].rr=tree[k<<1|1].rr;if(tree[k].rr==tree[k<<1|1].r-tree[k<<1|1].l+1)tree[k].rr+=tree[k<<1].rr;tree[k].all=max(tree[k<<1].rr+tree[k<<1|1].ll,max(tree[k<<1].all,tree[k<<1|1].all)); }void pushdown(int k) {tree[k<<1].lazy=tree[k<<1|1].lazy=tree[k].lazy;tree[k<<1].all=tree[k<<1].ll=tree[k<<1].rr=tree[k<<1].lazy*(tree[k<<1].r-tree[k<<1].l+1);tree[k<<1|1].all=tree[k<<1|1].ll=tree[k<<1|1].rr=tree[k<<1|1].lazy*(tree[k<<1|1].r-tree[k<<1|1].l+1);tree[k].lazy=-1; }void update(int k,int l,int r,int val) {if(tree[k].r<l||tree[k].l>r)return;if(tree[k].r<=r&&tree[k].l>=l){tree[k].all=tree[k].ll=tree[k].rr=val*(tree[k].r-tree[k].l+1);tree[k].lazy=val;return;}if(tree[k].lazy!=-1)pushdown(k);update(k<<1,l,r,val);update(k<<1|1,l,r,val);pushup(k); }int query(int k,int len) {if(tree[k].l==tree[k].r)//對葉子節點特判,不然過不了len為1的區間,不過這個題可能是數據水,不特判也能過。。if(len==1)return tree[k].ll;if(tree[k].all==0)//剪枝return 0;if(tree[k].lazy!=-1)pushdown(k);if(tree[k<<1].all>=len)//上述左區間return query(k<<1,len);else if(tree[k<<1].rr+tree[k<<1|1].ll>=len)//上述跨越中間點的區間return tree[k<<1].r-tree[k<<1].rr+1;else if(tree[k<<1|1].all>=len)//上述右區間return query(k<<1|1,len);else//沒有滿足的區間return 0; }int main() { // freopen("input.txt","r",stdin);int n,m;while(scanf("%d%d",&n,&m)!=EOF){build(1,1,n);while(m--){int a;scanf("%d",&a);if(a==1){int x;scanf("%d",&x);int temp=query(1,x);cout<<temp<<endl;if(temp)update(1,temp,temp+x-1,0);}else{int x,len;scanf("%d%d",&x,&len);update(1,x,x+len-1,1);}}}return 0; }照著大佬的代碼封裝了一下:(主要是pushup函數和pushdown函數看起來清爽了一點)
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<algorithm> #include<stack> #include<queue> #include<map> #include<sstream> #include<cmath> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=5e4+100;struct Node {int l,r;int ll,rr,all;int lazy;int mid(){return (l+r)>>1;}int cal_len(){return r-l+1;}void update_len(){all=rr=ll=lazy?cal_len():0;} }tree[N<<2];void build(int k,int l,int r) {tree[k].l=l;tree[k].r=r;tree[k].all=tree[k].ll=tree[k].rr=tree[k].cal_len();tree[k].lazy=-1;if(l==r)return;int mid=tree[k].mid();build(k<<1,l,mid);build(k<<1|1,mid+1,r); }void pushup(int k) {tree[k].ll=tree[k<<1].ll;if(tree[k].ll==tree[k<<1].cal_len())tree[k].ll+=tree[k<<1|1].ll;tree[k].rr=tree[k<<1|1].rr;if(tree[k].rr==tree[k<<1|1].cal_len())tree[k].rr+=tree[k<<1].rr;tree[k].all=max(tree[k<<1].rr+tree[k<<1|1].ll,max(tree[k<<1].all,tree[k<<1|1].all)); }void pushdown(int k) {tree[k<<1].lazy=tree[k<<1|1].lazy=tree[k].lazy;tree[k<<1].update_len();tree[k<<1|1].update_len();tree[k].lazy=-1; }void update(int k,int l,int r,int val) {if(tree[k].r<l||tree[k].l>r)return;if(tree[k].r<=r&&tree[k].l>=l){tree[k].lazy=val;tree[k].update_len();return;}if(tree[k].lazy!=-1)pushdown(k);update(k<<1,l,r,val);update(k<<1|1,l,r,val);pushup(k); }int query(int k,int len) {if(tree[k].l==tree[k].r)//對葉子節點特判,不然過不了len為1的區間,不過這個題可能是數據水,不特判也能過。。if(len==1)return tree[k].ll;if(tree[k].all==0)//剪枝return 0;if(tree[k].lazy!=-1)pushdown(k);if(tree[k<<1].all>=len)//上述左區間return query(k<<1,len);else if(tree[k<<1].rr+tree[k<<1|1].ll>=len)//上述跨越中間點的區間return tree[k<<1].r-tree[k<<1].rr+1;else if(tree[k<<1|1].all>=len)//上述右區間return query(k<<1|1,len);else//沒有滿足的區間return 0; }int main() { // freopen("input.txt","r",stdin);int n,m;while(scanf("%d%d",&n,&m)!=EOF){build(1,1,n);while(m--){int a;scanf("%d",&a);if(a==1){int x;scanf("%d",&x);int temp=query(1,x);cout<<temp<<endl;if(temp)update(1,temp,temp+x-1,0);}else{int x,len;scanf("%d%d",&x,&len);update(1,x,x+len-1,1);}}}return 0; }?
總結
以上是生活随笔為你收集整理的HDU - 3667 Hotel(线段树+区间合并)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HDU - 1540 Tunnel Wa
- 下一篇: HDU - 2871 Memory Co