洛谷 P3740 [HAOI2014]貼海報
題目描述
Bytetown城市要進行市長競選,所有的選民可以暢所欲言地對競選市長的候選人發表言論。為了統一管理,城市委員會為選民準備了一個張貼海報的electoral墻。
張貼規則如下:
-
electoral墻是一個長度為N個單位的長方形,每個單位記為一個格子;
-
所有張貼的海報的高度必須與electoral墻的高度一致的;
-
每張海報以“A B”表示,即從第A個格子到第B個格子張貼海報;
-
後貼的海報可以覆蓋前面已貼的海報或部分海報。
現在請你判斷,張貼完所有海報後,在electoral墻上還可以看見多少張海報。
輸入輸出格式
輸入格式:
第一行: N M 分別表示electoral墻的長度和海報個數
接下來M行: Ai Bi 表示每張海報張貼的位置
輸出格式:
輸出貼完所有海報後,在electoral墻上還可以看見的海報數。
這題其實挺不錯的,很典型的那種區間染色問題,所以記錄一下,以便日後復習。
解法一:線段樹
這種題一看就是線段樹,我們選擇邊判斷邊建樹,這樣就省去了build和pushdown。把線段樹用來存儲每條線段是否全部露出來,判斷的時候,如果目標線段裏有一個點沒有被擋住(即segtree[root]==0),那麽這條海報就會露出來,ans++,然後把整條線段染色後上推,由於我們剛剛對線段樹儲存元素的要求,所以上推法則就是:如果一個點有一個子樹有標記,那麽它也有標記。記得從最上面那個海報開始判斷,這樣應該就沒什麽問題了。
#include<cstdio> #include<iostream> using namespace std; int flag,sum[40000001],i,m,n; int a[10000001],b[10000001],ans; inline void pushup(int rt){ sum[rt]=sum[rt<<1]&&sum[rt<<1|1]; } inline void cck(int rt,int l,int r,int x,int y){ if (sum[rt]) return; if (x>r||y<l) returnView Code; if (x<=l&&r<=y){ flag=1; sum[rt]=1; return; } int mid=(l+r)>>1; if (mid>=x) cck(rt<<1,l,mid,x,y); if (mid<r) cck(rt<<1|1,mid+1,r,x,y); pushup(rt); } int main(){ scanf("%d%d",&m,&n); m=0; for (i=1; i<=n; i++){ scanf("%d%d",&a[i],&b[i]); m=max(m,b[i]); } for (i=n; i>=1; i--){ flag=0; cck(1,1,m,a[i],b[i]); if (flag) ans++; } printf("%d",ans); return 0; }
解法二:浮水法
這是一種專門解決區間染色問題的方法,思路大概是這樣:
還是倒著判斷,找到與海報i相交的海報j,不管他們的公共部分(因為被j擋住了),之後判斷它們不相交的部分(這時i的面積變成了公共部分的面積,可能有兩個公共部分,這裏由遞歸實現),方法詳見代碼註釋。
inline void water(int l,int r,int now,int p){ //p就是i,now用來枚舉j,l,r是當前判斷i的邊界,a是左邊界,b是右邊界
if (vis[p]) return; //vis用於快速推出遞歸 while (now<=n&&(l>=b[now]||r<=a[now])) now++; //這裏是判斷相交 if (now>n){ //now>n則沒有海報擋得住i了 ans++; vis[p]=1; return; } if (l<a[now]&&r>a[now]) water(l,a[now],now+1,p); //不好講,要不,自己畫個圖模擬一下? if (r>b[now]&&l<b[now]) water(b[now],r,now+1,p); //同上? }
完整代碼
#include<cstdio> #include<iostream> using namespace std; int vis[10000001],a[10000001],b[1000001]; int ans,n,m,i; inline void water(int l,int r,int now,int p){ if (vis[p]) return; while (now<=n&&(l>=b[now]||r<=a[now])) now++; if (now>n){ ans++; vis[p]=1; return; } if (l<a[now]&&r>a[now]) water(l,a[now],now+1,p); if (r>b[now]&&l<b[now]) water(b[now],r,now+1,p); } int main(){ scanf("%d%d",&m,&n); for (i=1; i<=n; i++){ scanf("%d%d",&a[i],&b[i]); b[i]++; } vis[n]=1; ans=1; for (i=n-1; i>=1; i--){ water(a[i],b[i],i+1,i); } printf("%d",ans); return 0; }View Code
洛谷 P3740 [HAOI2014]貼海報