題目大意
給定
題解
解題的方向肯定是先給區間排個序然後考慮dp.
我們將區間按左端點爲第一關鍵詞,右端點爲第二關鍵詞從小到大排序.
設第
爲什麼叫”支配”呢?
因爲如果選了區間
那麼我們的目標就變成了需要每個區間都被所選的區間支配到.
似乎有點問題?
請看下面的手繪渣圖:
這樣區間
然鵝因爲最終區間
關於如何求每個
爲了防止下標越界,不妨在首尾加入兩個區間
由於是按左端點排序,所以所有區間滿足左端點單調遞增,於是可以二分求
而左邊是求最大的下標
經過上面的分析,這個題目其實已經可以拋掉給出的區間不管了,完全轉化成了序列上的dp問題.
接下來把原來的區間都看作點好了.
由於某種不可抗力,我們需要倒着dp(下面會解釋原因).
令
這樣的狀態定義是充分的,因爲倒着來的話
而如果正着來,就利用不了這個單調性,也就不好進行狀態轉移了(官方題解中是一開始把區間按右端點當做第一關鍵詞排序然後正着dp的,道理是一樣的).
顯然第一維
dp的初始狀態爲
容易寫出狀態轉移方程:
然後用線段樹優化就可以了.
總的時間複雜度是
補:後來發現其實轉化後的問題就是選出一些區間覆蓋所有的點,重新給區間排序也不會有影響.
代碼
注:其實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;
}