CodeForces - 1285E Delete a Segmen(线段树+区间合并+离散化)
題目鏈接:點(diǎn)擊查看
題目大意:給出n個(gè)線段代表集合,現(xiàn)在問(wèn)若可以將其中任意一個(gè)線段刪除,則能夠形成最多多少個(gè)獨(dú)立的集合(取并集后)
題目分析:看到區(qū)間不難想到線段樹(shù)了,雖然這個(gè)題也可以用stl貪心做,但需要考慮的因素太多了,還是用線段樹(shù)直接莽吧,雖然前期的理論已經(jīng)準(zhǔn)備的很充分了,但在中途寫線段樹(shù)的時(shí)候還是寫崩了,最開(kāi)始的那個(gè)線段樹(shù)沒(méi)有想太多,直接更新到葉子結(jié)點(diǎn),TLE,加個(gè)lazy優(yōu)化一下吧,但優(yōu)化的不到位,TLE,最后還是參考了zx學(xué)長(zhǎng)的代碼,豁然開(kāi)朗,想明白了對(duì)于這個(gè)題目的幾個(gè)關(guān)鍵的點(diǎn),沖著這幾個(gè)點(diǎn)改了一下線段樹(shù)的內(nèi)部實(shí)現(xiàn),然后就A了
上面說(shuō)的是這個(gè)題的心路歷程,回到這個(gè)題目來(lái),最直接的想法就是用線段樹(shù)維護(hù)一個(gè)cover變量,表示每個(gè)區(qū)間被覆蓋的次數(shù),再單獨(dú)維護(hù)一個(gè)sum變量,用來(lái)表示區(qū)間內(nèi)有多少個(gè)獨(dú)立的集合,也就是不接壤的線段,最后就是維護(hù)一個(gè)布爾類型的ll和rr,分別表示左端點(diǎn)和右端點(diǎn)是否被覆蓋,在區(qū)間合并的時(shí)候要用到
具體該如何使用上面的變量呢,首先將n個(gè)線段讀入到線段樹(shù)中,此后以此枚舉每個(gè)線段刪除之后的答案,根據(jù)上面變量的定義,顯然答案就是tree[1].sum,所以對(duì)于更新來(lái)說(shuō)我們只需要讓當(dāng)前枚舉的線段的cover減一即可,最后別忘了回溯,時(shí)間復(fù)雜度是nlogn級(jí)別的,帶點(diǎn)常數(shù)無(wú)傷大雅
這個(gè)題的關(guān)鍵就是區(qū)間的范圍給的太大了,有2e9那么多,只能離散化,但面臨的一個(gè)問(wèn)題是,離散化后只剩下了端點(diǎn)的情況,端點(diǎn)內(nèi)部的區(qū)間都被離散化掉了,但在這個(gè)題目中我們是需要用到的,所以在離散化的時(shí)候?qū)^(qū)間內(nèi)、區(qū)間左、區(qū)間右這三個(gè)位置順便一起離散化一下就可以了,相當(dāng)于將區(qū)間內(nèi)的所有空白點(diǎn)壓縮成了一個(gè)點(diǎn),具體實(shí)現(xiàn)就是讓端點(diǎn)都乘以二,這樣既能解決題目中相鄰區(qū)間不相交的問(wèn)題,如[1,4][5,6]就屬于兩個(gè)集合,而不是一個(gè)集合,也能解決上述離散化的問(wèn)題,一舉兩得
講完離散化后我們就可以直接寫線段樹(shù)了,具體實(shí)現(xiàn)還是看代碼吧,打上注釋了都,只可意會(huì)不可言談
最后說(shuō)一下這個(gè)題的幾個(gè)關(guān)鍵點(diǎn)吧,因?yàn)槲覀冏罱K需要的是tree[1].sum,換句話說(shuō),只有涉及到最終答案的值才需要更新一下,假如某個(gè)區(qū)間已經(jīng)被更新了,那么其接下來(lái)的子樹(shù)就無(wú)需遍歷了,因?yàn)橹懒水?dāng)前區(qū)間的狀態(tài),就足以向上維護(hù)最終答案了,而對(duì)于lazy變量,在這個(gè)題目中實(shí)際上就是cover變量,因?yàn)檫@個(gè)題目無(wú)需向下傳遞任何信息,只需要向上傳遞信息用來(lái)維護(hù)最終答案,所以在這個(gè)題目中的cover變量的用途具體也是用來(lái)維護(hù)狀態(tài)的
代碼:
#include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cstring> #include<algorithm> #include<stack> #include<queue> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=2e5+100;struct Node {int l,r,sum,cover;//sum:有多少個(gè)區(qū)間段 cover:被覆蓋了幾次 bool ll,rr;//左右端點(diǎn)是否被覆蓋 }tree[N<<5]; void pushup(int k) {if(tree[k].cover)//如果當(dāng)前區(qū)間被覆蓋,直接更新數(shù)據(jù) {tree[k].sum=1;tree[k].ll=tree[k].rr=true;}else//如果沒(méi)被覆蓋,則用子節(jié)點(diǎn)更新 {tree[k].ll=tree[k<<1].ll;tree[k].rr=tree[k<<1|1].rr;tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;if(tree[k<<1].rr&&tree[k<<1|1].ll)//如果左子樹(shù)的右端點(diǎn)和右子樹(shù)的左端點(diǎn)同時(shí)被覆蓋,則區(qū)間段減一,有點(diǎn)區(qū)間合并的意思 tree[k].sum--;} }void init(int k,int l,int r) {tree[k].ll=tree[k].rr=false;tree[k].l=l;tree[k].r=r;tree[k].sum=tree[k].cover=0; }void build(int k,int l,int r) {init(k,l,r);if(l==r)//葉子結(jié)點(diǎn)需要特別更新一下其子節(jié)點(diǎn),因?yàn)橄旅鎢pdate函數(shù)在葉子結(jié)點(diǎn)時(shí)可能直接調(diào)用pushup,若沒(méi)有初始化則會(huì)因?yàn)橹暗臄?shù)據(jù)沒(méi)有初始化而出錯(cuò){init(k<<1,l,r);init(k<<1|1,l,r);return;}int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r); }void update(int k,int l,int r,int val) {if(l>tree[k].r||r<tree[k].l)return;if(tree[k].l>=l&&tree[k].r<=r){tree[k].cover+=val;pushup(k);return;}int mid=tree[k].l+tree[k].r>>1;update(k<<1,l,r,val);update(k<<1|1,l,r,val);pushup(k);//記得上傳狀態(tài) }vector<int>v;//離散化用 int get_id(int x) {return lower_bound(v.begin(),v.end(),x)-v.begin(); }void dis_create() {v.push_back(-inf);sort(v.begin(),v.end());v.erase(unique(v.begin(),v.end()),v.end()); }struct Section {int l,r;void input()//記得除了左右端點(diǎn)還需要加上一些空端點(diǎn),不然不好判斷區(qū)間段的連續(xù)性 {scanf("%d%d",&l,&r);v.push_back(2*l);v.push_back(2*r);v.push_back(2*l-1);v.push_back(2*r+1);v.push_back(2*l+1);}void dis_create()//更新離散化后的id {l=get_id(2*l);r=get_id(2*r);} }a[N];int main() { // freopen("input.txt","r",stdin);int w;cin>>w;while(w--){v.clear();int n;scanf("%d",&n);for(int i=1;i<=n;i++)a[i].input();dis_create();for(int i=1;i<=n;i++)//將區(qū)間端點(diǎn)離散化 a[i].dis_create();build(1,1,v.size());for(int i=1;i<=n;i++)update(1,a[i].l,a[i].r,1);int ans=0;for(int i=1;i<=n;i++){update(1,a[i].l,a[i].r,-1);ans=max(ans,tree[1].sum);update(1,a[i].l,a[i].r,1);}printf("%d\n",ans);}return 0; }?
總結(jié)
以上是生活随笔為你收集整理的CodeForces - 1285E Delete a Segmen(线段树+区间合并+离散化)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: CodeForces - 1285D D
- 下一篇: URAL - 1099 Work Sch