神仙题。

题意

有n个区间$[l_i,r_i]$,你需要选出一些区间,使得:

如果把每个区间当成一个点,把相交的区间对应的点连边,那么这些区间形成了一颗

$n\le 2000$

题解

注意到满足条件的区间一定是外层的“大区间”依次相交内层可能含有互不接触的“小区间”。

图片

先把区间按照左端点排序。

设$dp_{i,j}$表示已经考虑了前i-1个区间,区间最右端点是j的方案数。设$nxt_i$是左端点$>i$的第一个区间(没有就是n+1)。

从前往后转移。

  1. 区间i不选择:右端点不会变,下一个考虑的是i+1。$dp_{i+1,j}+=dp_{i,j}$。
  2. 区间i选择(能放进去,并且是小区间,由于$l_i,r_i$区间就会覆盖两次,那么下一个可以选择的区间必须左端点$>r_i$,所以$dp_{nxt_{ri},j}+=dp_{i,j}$。
  3. (能放进去的情况下)区间i是大区间,由于$l_i,j$区间就会覆盖两次,下一个选择的区间左端点必须$> j$。同时总共的右端点就会变成$r_i$,所以$dp_{nxt_j,r_i}+=dp_{i,j}$。
  4. 特别的,对于第一个放i区间的情况,$dp_{i+1,r_i}+=1$。

那么答案就是$\sum\limits_i dp_{n+1,i}$

/*
Author: CNYALI_LK
LANG: C++
PROG: b.cpp
Mail: cnyalilk@vip.qq.com
*/
#include<bits/stdc++.h>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define DEBUG printf("Passing [%s] in LINE %d\n",__FUNCTION__,__LINE__)
#define Debug debug("Passing [%s] in LINE %d\n",__FUNCTION__,__LINE__)
#define all(x) x.begin(),x.end()
#define x first
#define y second
using namespace std;
const double eps=1e-8;
const double pi=acos(-1.0);
typedef long long ll;
typedef pair<int,int> pii;
template<class T>int chkmin(T &a,T b){return a>b?a=b,1:0;}
template<class T>int chkmax(T &a,T b){return a<b?a=b,1:0;}
template<class T>T sqr(T a){return a*a;}
template<class T>T mmin(T a,T b){return a<b?a:b;}
template<class T>T mmax(T a,T b){return a>b?a:b;}
template<class T>T aabs(T a){return a<0?-a:a;}
template<class T>int dcmp(T a,T b){return a>b;}
template<int *a>int cmp_a(int x,int y){return a[x]<a[y];}
#define min mmin
#define max mmax
#define abs aabs
int read(){
    int s=0,base=1;
    char c;
    while(!isdigit(c=getchar()))if(c=='-')base=-base;
    while(isdigit(c)){s=s*10+(c^48);c=getchar();}
    return s*base;
}
int f[2047][4095],nxt[4095];
const int p=1000000007;
pii a[2047];
void Add(int &x,int y){if((x+=y)>=p)x-=p;}
int main(){
#ifdef cnyali_lk
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);
#endif
    int n;    
    n=read();
    for(int i=1;i<=n;++i){
        a[i].x=read();a[i].y=read();
    }
    sort(a+1,a+n+1);
    a[n+1].x=19260817;
    for(int i=1,j=1;i<=4000;nxt[i]=j,++i)while(a[j].x<=i)++j;
    for(int i=1;i<=n;++i){
        Add(f[i+1][a[i].y],1);    
        for(int j=1;j<=4000;++j)if(f[i][j]){
            Add(f[i+1][j],f[i][j]);
            if(j>=a[i].x){
                if(a[i].y<=j){
                    Add(f[nxt[a[i].y]][j],f[i][j]);
                }    
                else Add(f[nxt[j]][a[i].y],f[i][j]);
            }
        }
    }
    int ans=0;
    for(int i=1;i<=4000;++i)Add(ans,f[n+1][i]);
    printf("%d\n",ans);
    return 0;
}

标签: DP

添加新评论