1. 程式人生 > >洛谷 P3740 [HAOI2014]貼海報

洛谷 P3740 [HAOI2014]貼海報

什麽 sum 現在 pac mes col closed oot show

題目描述

Bytetown城市要進行市長競選,所有的選民可以暢所欲言地對競選市長的候選人發表言論。為了統一管理,城市委員會為選民準備了一個張貼海報的electoral墻。

張貼規則如下:

  1. electoral墻是一個長度為N個單位的長方形,每個單位記為一個格子;

  2. 所有張貼的海報的高度必須與electoral墻的高度一致的;

  3. 每張海報以“A B”表示,即從第A個格子到第B個格子張貼海報;

  4. 後貼的海報可以覆蓋前面已貼的海報或部分海報。

現在請你判斷,張貼完所有海報後,在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) return
; 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; }
View Code

解法二:浮水法

這是一種專門解決區間染色問題的方法,思路大概是這樣:

還是倒著判斷,找到與海報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]貼海報