日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

CodeForces - 1285E Delete a Segmen(线段树+区间合并+离散化)

發布時間:2024/4/11 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 CodeForces - 1285E Delete a Segmen(线段树+区间合并+离散化) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目鏈接:點擊查看

題目大意:給出n個線段代表集合,現在問若可以將其中任意一個線段刪除,則能夠形成最多多少個獨立的集合(取并集后)

題目分析:看到區間不難想到線段樹了,雖然這個題也可以用stl貪心做,但需要考慮的因素太多了,還是用線段樹直接莽吧,雖然前期的理論已經準備的很充分了,但在中途寫線段樹的時候還是寫崩了,最開始的那個線段樹沒有想太多,直接更新到葉子結點,TLE,加個lazy優化一下吧,但優化的不到位,TLE,最后還是參考了zx學長的代碼,豁然開朗,想明白了對于這個題目的幾個關鍵的點,沖著這幾個點改了一下線段樹的內部實現,然后就A了

上面說的是這個題的心路歷程,回到這個題目來,最直接的想法就是用線段樹維護一個cover變量,表示每個區間被覆蓋的次數,再單獨維護一個sum變量,用來表示區間內有多少個獨立的集合,也就是不接壤的線段,最后就是維護一個布爾類型的ll和rr,分別表示左端點和右端點是否被覆蓋,在區間合并的時候要用到

具體該如何使用上面的變量呢,首先將n個線段讀入到線段樹中,此后以此枚舉每個線段刪除之后的答案,根據上面變量的定義,顯然答案就是tree[1].sum,所以對于更新來說我們只需要讓當前枚舉的線段的cover減一即可,最后別忘了回溯,時間復雜度是nlogn級別的,帶點常數無傷大雅

這個題的關鍵就是區間的范圍給的太大了,有2e9那么多,只能離散化,但面臨的一個問題是,離散化后只剩下了端點的情況,端點內部的區間都被離散化掉了,但在這個題目中我們是需要用到的,所以在離散化的時候將區間內、區間左、區間右這三個位置順便一起離散化一下就可以了,相當于將區間內的所有空白點壓縮成了一個點,具體實現就是讓端點都乘以二,這樣既能解決題目中相鄰區間不相交的問題,如[1,4][5,6]就屬于兩個集合,而不是一個集合,也能解決上述離散化的問題,一舉兩得

講完離散化后我們就可以直接寫線段樹了,具體實現還是看代碼吧,打上注釋了都,只可意會不可言談

最后說一下這個題的幾個關鍵點吧,因為我們最終需要的是tree[1].sum,換句話說,只有涉及到最終答案的值才需要更新一下,假如某個區間已經被更新了,那么其接下來的子樹就無需遍歷了,因為知道了當前區間的狀態,就足以向上維護最終答案了,而對于lazy變量,在這個題目中實際上就是cover變量,因為這個題目無需向下傳遞任何信息,只需要向上傳遞信息用來維護最終答案,所以在這個題目中的cover變量的用途具體也是用來維護狀態的

代碼:

#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:有多少個區間段 cover:被覆蓋了幾次 bool ll,rr;//左右端點是否被覆蓋 }tree[N<<5]; void pushup(int k) {if(tree[k].cover)//如果當前區間被覆蓋,直接更新數據 {tree[k].sum=1;tree[k].ll=tree[k].rr=true;}else//如果沒被覆蓋,則用子節點更新 {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)//如果左子樹的右端點和右子樹的左端點同時被覆蓋,則區間段減一,有點區間合并的意思 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)//葉子結點需要特別更新一下其子節點,因為下面update函數在葉子結點時可能直接調用pushup,若沒有初始化則會因為之前的數據沒有初始化而出錯{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);//記得上傳狀態 }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()//記得除了左右端點還需要加上一些空端點,不然不好判斷區間段的連續性 {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++)//將區間端點離散化 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; }

?

總結

以上是生活随笔為你收集整理的CodeForces - 1285E Delete a Segmen(线段树+区间合并+离散化)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。