1. 程式人生 > >【線段樹】【P3740】 [HAOI2014]貼海報

【線段樹】【P3740】 [HAOI2014]貼海報

直接 中間 tput NPU size 長度 插入 image 所有

傳送門

Description

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

張貼規則如下:

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

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

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

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

現在請你判斷,張貼完所有海報後,在electoral墻上還可以看見多少張海報。

Input

  第一行: N M 分別表示electoral墻的長度和海報個數

  接下來M行: Ai Bi 表示每張海報張貼的位置

Output

  輸出貼完所有海報後,在electoral墻上還可以看見的海報數。

Sample Input

100 5
1 4
2 6
8 10
3 4
7 10

Sample Output

4

Hint 

  10<= N <= 10000000 1<=M<=1000 1<= Ai <= Bi <=10000000

  所有的數據都是整數。數據之間有一個空格

Solution

  考慮暴力修改 上界復雜度將達到O(nm)。顯然扯淡。

  考慮進行優化。由於是對區間操作。最後輸出答案也相當於對區間[1,n]進行詢問,於是自然想到對區間操作最拿手的線段樹。

  如果在線修改,記錄區間上有幾個廣告,以及區間上廣告是否覆蓋完全(即最右側的廣告在區間外是否還有一塊),合並時f[p]=f[dp]+f[ddp](+1),會產生問題:如樣例圖片:

          技術分享圖片

觀察樣例中間被英文標出的地方,使用上述算法,改區間會被判定為有三個廣告。但事實上只有兩個,因為是上面覆蓋了下面的一部分。

  如何解決此問題?考慮廣告數量太大無法狀壓,但不強制在線,於是我們把廣告都讀進來,倒序插入。因為較早貼上的廣告顯然無法影響到後面的廣告。這裏再次使用了階段劃分和無後效性的思想

。對於一個區間,如果已經被提前計算的廣告完全覆蓋,則直接return。一個廣告能被看見,當且僅當他能覆蓋一個獨立的區間。至此,問題得解。

Code

#include<cstdio>
#include<algorithm>
#define ci const int
#define maxn 10000010
#define maxt 40000010

inline void qr(int &x) {
    char ch=getchar(),last= ;
    while(ch>9||ch<0) last=ch,ch=getchar();
    while(ch>=0&&ch<=9)    x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    if(last==-)    x=-x;
}

inline int max(const int &a,const int &b) {if(a>b) return a;return b;}
inline int min(const int &a,const int &b) {if(a<b) return a;return b;}
inline int abs(const int &a) {if(a>=0) return a;return -a;}

inline void swap(int &a,int &b) {int temp=a;a=b;b=temp;}

int n,m,a,b,cnt;

bool tree[maxt],judge;

struct M {
    int l,r;
};
M MU[maxn];

void change(ci l,ci r,ci p,ci aiml,ci aimr) {
    if(l>r) return;
    if(l>aimr||r<aiml) return;
    if(l>=aiml&&r<=aimr) {if(!tree[p]) tree[p]=judge=true;return;}
    if(tree[p])    return;
    int mid=(l+r)>>1,dp=p<<1,ddp=dp|1;
    change(l,mid,dp,aiml,aimr);change(mid+1,r,ddp,aiml,aimr);
    tree[p]=tree[dp]&&tree[ddp];
}

int main() {
    qr(n);qr(m);
    for(int i=1;i<=m;++i) {qr(MU[i].l);qr(MU[i].r);}
    do {
        judge=false;
        change(1,n,1,MU[m].l,MU[m].r);
        if(judge) ++cnt;
    } while(--m);
    printf("%d\n",cnt);
    return 0;
}

Summary

  1、階段劃分是做任何題十分有力的工具,除開DP外,也要時刻應用。
  2、在線難以處理時,考慮離線。正序難以處理時,考慮倒序。無序難以處理時,考慮有序。因為後者往往在一定程度上滿足無後效性。

【線段樹】【P3740】 [HAOI2014]貼海報