HDU - 2871 Memory Control(线段树+区间合并)好题!
題目鏈接:點擊查看
題目大意:給定n個內(nèi)存和m個操作,分別是:
題目分析:
先對這個題吐槽一下吧,本來以為昨天晚上做的hotle那個題就已經(jīng)很惡心了,寫的代碼又臭又長,結(jié)果今天這個題刷新了我對線段樹的認(rèn)知,這個題暫時讓他當(dāng)我遇到過的最惡心的題目應(yīng)該沒問題了。從早晨九點不到一直做到下午一點多,被惡心的不要不要的,都有點懷疑自己了。
對于上述四個操作挨個分析吧:
對于New這個操作,簡直和hotle那個題目的那個一模一樣啊,就是讓求最左端符合條件的區(qū)間,我們用區(qū)間合并就可以實現(xiàn),在update函數(shù)和pushup函數(shù)中維護(hù)ll,rr和all三個變量的值,這三個值分別代表從區(qū)間左端點開始的最長連續(xù)長度,從區(qū)間右端點開始的最長連續(xù)長度和區(qū)間中的最長連續(xù)長度,維護(hù)好這三個值后再在query函數(shù)中依次查找符合條件的區(qū)間,若滿足向下遞歸,若得到答案則向上返回答案,都是套路了,一會看代碼就好。
對于Free操作,有個難點就是如何找到連續(xù)區(qū)間的首地址和尾地址,這里我的方法是在線段樹的每個節(jié)點中,維護(hù)兩個變量:st和ed,分別記錄該節(jié)點所屬的連續(xù)區(qū)間中的首地址與尾地址,那么也就引出了我們需要對于每個Free的x求出所對應(yīng)的節(jié)點編號才能隨意訪問其節(jié)點,這里我用的一個getID的函數(shù),類比于點查詢的操作向下遞歸,只不過返回值改成了該點的節(jié)點編號,時間復(fù)雜度為logn,畢竟是二分查找嘛。。那么現(xiàn)在我們已經(jīng)求出了該節(jié)點的編號,就可以訪問他的首地址和尾地址了,那么接下來需要考慮的是如何初始化以及維護(hù)這兩個變量,因為這個題目中的n個內(nèi)存都是大于0的,我們可以將其設(shè)置為0,即st=ed=0,代表該節(jié)點沒有位于連續(xù)內(nèi)存中,當(dāng)用update函數(shù)更新一段區(qū)間為連續(xù)內(nèi)存是,將這段內(nèi)存的首地址和尾地址都分別設(shè)置為區(qū)間的左右端點,等更新的時候用pushdown函數(shù)向下傳遞就好,為什么不用向上傳遞呢?因為當(dāng)用update函數(shù)更新的時候,遇到符合條件的區(qū)間就不會向下遞歸了,所以這段區(qū)間就已經(jīng)滿足條件了,我們只需要將這段區(qū)間下面的節(jié)點都更新上首尾地址即可。
對于Get操作,網(wǎng)上有一種很麻煩但跑起來很快的方法,我看了半天還是沒怎么看懂,大體意思就是在節(jié)點中在維護(hù)一個size變量,用來記錄當(dāng)前區(qū)間中含有多少個連續(xù)的內(nèi)存,這樣的話就可以直接對size進(jìn)行二分查找(也是類似于點查詢的一種),找到具體的節(jié)點然后返回首地址即可,這里我是看懂了,只不過在free函數(shù)里面每次釋放節(jié)點都需要更新size的值,這里我不太會,加上關(guān)于size的傳遞我也被繞暈了。。感興趣的話可以去別人家的博客學(xué)習(xí)一下。關(guān)于這個題,想要實現(xiàn)這個操作我選擇了用時間付出點代價,用了stl中的set類,主要是因為set可以實現(xiàn)自動排序,每次遍歷一邊找到第x個數(shù),直接輸出即可,而在保存的時候也是直接插入即可,他在內(nèi)部會自動排序,free函數(shù)中釋放后也只需要刪除即可,其余的操作就不用我們管了,這樣Get操作也變得簡單易實現(xiàn)了。
最后就剩下了一個Reset操作了,這個操作不用多說了吧,直接用upset更新一下就好了
還有注意一下輸出格式
其實用stl一開始還是擔(dān)心會被卡時間的,比如給出5e5個內(nèi)存,然后5e5個操作,每個操作都是刪除一個數(shù)然后增加一個數(shù),感覺很有可能超時,或者是給出5e5個數(shù),每次都Get最后一個,那時間復(fù)雜度就變成了至少1e10了,不過還好謝天謝地這個題目沒有卡這些點,讓本蒟蒻順利鉆空子過了。。?
2021.6.27 UPDATE:
考試周不想復(fù)習(xí)突然看到了這個題目,讀了一遍代碼發(fā)現(xiàn)題目數(shù)據(jù)水了,把之前寫的 O(n*n) 的代碼給放過去了。。
其實正解也不難,瓶頸在于:
用線段樹實現(xiàn)就好了,懶得重構(gòu)代碼了,繼續(xù)復(fù)習(xí)數(shù)據(jù)庫去啦
上代碼:
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<algorithm> #include<stack> #include<queue> #include<map> #include<sstream> #include<set> #include<cmath> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=5e4+100;set<int>vis;set<int>::iterator it;struct Node {int l,r;//左右區(qū)間端點int ll,rr,all;//從左端點開始的最長連續(xù)長度,從右端點開始的最長連續(xù)長度,區(qū)間內(nèi)最長連續(xù)長度int st,ed;//首地址和尾地址int lazy;//懶標(biāo)記int mid(){return (l+r)>>1;}int cal_len(){return r-l+1;}void update_len(){ll=rr=all=lazy*cal_len();} }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].st=tree[k].ed=0;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;tree[k].rr=tree[k<<1|1].rr;if(tree[k].ll==tree[k<<1].cal_len())tree[k].ll+=tree[k<<1|1].ll;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].st=tree[k<<1|1].st=tree[k].st;tree[k<<1].ed=tree[k<<1|1].ed=tree[k].ed;tree[k<<1].lazy=tree[k<<1|1].lazy=tree[k].lazy;tree[k].lazy=-1;tree[k<<1].update_len();tree[k<<1|1].update_len(); } void update(int k,int l,int r,int val,int flag)//k是節(jié)點編號,l和r是左右區(qū)間,val代表是否被占用,1代表未被占用,0代表被占用、flag和val的意義相似,1代表被占用,0代表未被占用 {if(tree[k].l>r||tree[k].r<l)return;if(tree[k].l>=l&&tree[k].r<=r){tree[k].lazy=val;tree[k].update_len();if(flag){tree[k].st=l;tree[k].ed=r;}else{tree[k].st=tree[k].ed=0;}return;}if(tree[k].lazy!=-1)pushdown(k);update(k<<1,l,r,val,flag);update(k<<1|1,l,r,val,flag);pushup(k); }int getID(int k,int pos)//通過節(jié)點位置得到節(jié)點編號的函數(shù) {if(tree[k].l==tree[k].r)return k;if(tree[k].lazy!=-1)pushdown(k);int mid=tree[k].mid();if(mid>=pos)return getID(k<<1,pos);elsereturn getID(k<<1|1,pos); }int query(int k,int len)//查詢可用空間并返回其首地址 {if(tree[k].l==tree[k].r)if(len==1)return tree[k].ll;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);} }int main() { // freopen("input.txt","r",stdin);int n,m;while(scanf("%d%d",&n,&m)!=EOF){vis.clear();//多組輸入記得初始化build(1,1,n);char s[10];while(m--){scanf("%s",s);if(s[0]=='N'){int len;scanf("%d",&len);if(len>tree[1].all)cout<<"Reject New"<<endl;else{int temp=query(1,len);printf("New at %d\n",temp);update(1,temp,temp+len-1,0,1);vis.insert(temp);}}else if(s[0]=='R'){cout<<"Reset Now"<<endl;update(1,1,n,1,0);vis.clear();}else if(s[0]=='G'){int x;scanf("%d",&x);int cnt=0;for(it=vis.begin();it!=vis.end();it++){cnt++;if(cnt==x)break;}if(cnt<x)cout<<"Reject Get"<<endl;elseprintf("Get at %d\n",*it);}else if(s[0]=='F'){int x;scanf("%d",&x);int cnt=getID(1,x);if(tree[cnt].st==0)cout<<"Reject Free"<<endl;else{printf("Free from %d to %d\n",tree[cnt].st,tree[cnt].ed);vis.erase(tree[cnt].st);update(1,tree[cnt].st,tree[cnt].ed,1,0);}}}cout<<endl;//記得輸出一個回車防止PE}return 0; }總結(jié)
以上是生活随笔為你收集整理的HDU - 2871 Memory Control(线段树+区间合并)好题!的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HDU - 3667 Hotel(线段树
- 下一篇: HDU - 5475 An easy p