計蒜客 微軟項目經理的挑選方案

題目鏈接

題目大意

給定n 個互不相同的閉區間[li,ri] ,現在從中選出一些區間,滿足對於任意未選出的區間,都有至少一個選出的區間與其有交集,求可能的方案數,對109+7 取模.

1n2×105,1li<ri109.

題解

解題的方向肯定是先給區間排個序然後考慮dp.

我們將區間按左端點爲第一關鍵詞,右端點爲第二關鍵詞從小到大排序.

設第i 個區間(1in )能支配的區間的極大區間是[L[i],R[i]] ,滿足i[L[i],R[i]] ,且這個區間內的所有區間都與區間i 有交集.
爲什麼叫”支配”呢?
因爲如果選了區間i ,那麼它所支配的區間都可以不選了.
那麼我們的目標就變成了需要每個區間都被所選的區間支配到.

似乎有點問題?
請看下面的手繪渣圖:

這樣區間i 往左邊應該只能支配到i 本身,但是如果選擇了i ,區間i2 也是可以不選的.
然鵝因爲最終區間i1 也要被支配到,而這樣區間i2 也會”順便”被支配到,所以看作區間i 無法支配到區間i2 也不會有影響.

關於如何求每個L[i],R[i] ,我們可以轉化爲求區間i 兩側最近的不能被i 支配的區間.
爲了防止下標越界,不妨在首尾加入兩個區間[0,0][,] .
由於是按左端點排序,所以所有區間滿足左端點單調遞增,於是可以二分求R[i] .
而左邊是求最大的下標j ,使得區間j 的右端點小於區間i 的左端點,這個東西是單調遞增的,可以用單調隊列求解(當然也有線性的方法).

經過上面的分析,這個題目其實已經可以拋掉給出的區間不管了,完全轉化成了序列上的dp問題.
接下來把原來的區間都看作點好了.

由於某種不可抗力,我們需要倒着dp(下面會解釋原因).
dp[i][j] 表示考慮是否選編號爲i ~n 的點,最後一個未被支配的點爲j 的方案數.
這樣的狀態定義是充分的,因爲倒着來的話L[i] 是單調遞減的,那麼如果選擇當前的i 點並且支配了最後一個未被支配的點j ,此時最後一個未被支配的點就變爲了L[i]1 ,可以完成狀態轉移.
而如果正着來,就利用不了這個單調性,也就不好進行狀態轉移了(官方題解中是一開始把區間按右端點當做第一關鍵詞排序然後正着dp的,道理是一樣的).

顯然第一維i 是可以去掉的.
dp的初始狀態爲dp[n]=1 ,要求的答案爲dp[0] .
容易寫出狀態轉移方程:

dp[j]={2dp[j]2dp[j]+R[i]k=L[i]dp[k]R[i]<jnj=L[i]1

然後用線段樹優化就可以了.

總的時間複雜度是O(nlgn) .


補:後來發現其實轉化後的問題就是選出一些區間覆蓋所有的點,重新給區間排序也不會有影響.

代碼

注:其實deque是系統關鍵詞,比較危險,請不要學我…

#include<cstdio>
#include<cstring>
#include<cassert>
#include<algorithm>
#define lowbit(x) (x&-x);
#define fi first
#define se second
using namespace std;
typedef pair<int,int> pii;
const int N=(int)2e5+5,mod=(int)1e9+7,INF=0x7fffffff;
int n,L[N],R[N],deque[N],pow_2[N];
pii itv[N];
struct Segment_Tree{
    #define lson (k<<1)
    #define rson (k<<1|1)
    struct Node{
        int L,R,Lsh_cnt,sum;
    }tree[N<<2];
    void build(int L=0,int R=n,int k=1){
        tree[k].L=L;
        tree[k].R=R;
        tree[k].Lsh_cnt=0;
        tree[k].sum=R==n;
        if(L==R)return;
        int mid=L+R>>1;
        build(L,mid,lson);
        build(mid+1,R,rson);
    }
    inline void push_up(int k){
        if((tree[k].sum=tree[lson].sum+tree[rson].sum)>=mod)
            tree[k].sum-=mod;
    }
    inline void mod_Lsh(int &num,int cnt){
        num=1ll*num*pow_2[cnt]%mod;
    }
    void push_down(int k){
        if(!tree[k].Lsh_cnt)return;
        mod_Lsh(tree[lson].sum,tree[k].Lsh_cnt);
        mod_Lsh(tree[rson].sum,tree[k].Lsh_cnt);
        tree[lson].Lsh_cnt+=tree[k].Lsh_cnt;
        tree[rson].Lsh_cnt+=tree[k].Lsh_cnt;
        tree[k].Lsh_cnt=0;
    }
    int query_sum(int L,int R,int k=1){
        if(tree[k].L==L&&tree[k].R==R)
            return tree[k].sum;
        push_down(k);
        int mid=tree[k].L+tree[k].R>>1;
        if(R<=mid)return query_sum(L,R,lson);
        if(L>mid)return query_sum(L,R,rson);
        int res=query_sum(L,mid,lson)+query_sum(mid+1,R,rson);
        if(res>=mod)res-=mod;
        return res;
    }
    void modify(int tar,int val,int k=1){
        if(tree[k].L==tree[k].R){
            tree[k].sum=val;
            return;
        }
        push_down(k);
        int mid=tree[k].L+tree[k].R>>1;
        if(tar<=mid)modify(tar,val,lson);
        else modify(tar,val,rson);
        push_up(k);
    }
    void Lsh(int L,int R,int k=1){
        if(tree[k].L==L&&tree[k].R==R){
            mod_Lsh(tree[k].sum,1);
            ++tree[k].Lsh_cnt;
            return;
        }
        push_down(k);
        int mid=tree[k].L+tree[k].R>>1;
        if(R<=mid)Lsh(L,R,lson);
        else if(L>mid)Lsh(L,R,rson);
        else{
            Lsh(L,mid,lson);
            Lsh(mid+1,R,rson);
        }
        push_up(k);
    }
}T;
void rd(int &res){
    res=0;
    char c;
    while(c=getchar(),c<48);
    do res=(res<<3)+(res<<1)+(c^48);
        while(c=getchar(),c>47);
}
void init(){
    itv[n]=pii(0,0);
    itv[n+1]=pii(INF,INF);
    sort(itv,itv+n+2);
    int head=0,tail=0;
    deque[tail++]=0;
    for(int i=1;i<=n;++i){
        for(int bin_L=i+1,bin_R=n+1;bin_L<=bin_R;){
            int mid=bin_L+bin_R>>1;
            if(itv[mid].fi>itv[i].se){
                R[i]=mid-1;
                bin_R=mid-1;
            }
            else bin_L=mid+1;
        }
        int bin_L=head,bin_R=tail-1,res;
        while(bin_L<=bin_R){
            int mid=bin_L+bin_R>>1;
            if(itv[deque[mid]].se<itv[i].fi){
                res=mid;
                bin_L=mid+1;
            }
            else bin_R=mid-1;
        }
        L[i]=deque[res]+1;
        head=res;
        while(head<tail&&itv[deque[tail-1]].se>=itv[i].se)--tail;
        deque[tail++]=i;
    }
    pow_2[0]=1;
    for(int i=1;i<=n;++i)
        if((pow_2[i]=pow_2[i-1]<<1)>=mod)pow_2[i]-=mod;
}
int main(){
    rd(n);
    for(int i=0;i<n;++i){
        rd(itv[i].fi);
        rd(itv[i].se);
    }
    init();
    T.build();
    for(int i=n;i;--i){
        if(R[i]<n)T.Lsh(R[i]+1,n);
        T.modify(L[i]-1,(1ll*T.query_sum(L[i],R[i])+(T.query_sum(L[i]-1,L[i]-1)<<1))%mod);
    }
    printf("%d\n",T.query_sum(0,0));
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章