[luogu5464]缩小社交圈

不难证明合法当且仅当满足一下两个条件:

1.每一个位置最多被覆盖两次(无环)

2.将选择的区间按左端点从小到大排序,对于每一个左端点,其之前的区间的最大右端点不小于其(连通)

(关于第一个的充分性证明可以考虑一个极小环,将环上区间排序即矛盾)

就将所有区间按照左端点从小到大排序,然后依次选择

更具体的,用$f_{i,j}$表示仅考虑左端点小于等于$i$的区间,最大右端点为$j$的选择方案数

转移考虑第$i$个位置上的区间,根据第二个条件可以得到$j\ge i$,再根据第一个条件可以得到最多选择一个区间,接下来,可以直接转移到$f_{\min(j,r_{选择的区间},\max(j,r_{选择的区间})}$

解释一下:由于选择了这个区间,那么剩下的区间左端点必然不能在$[i,\min(j,选择的区间)]$,否则该位置即被覆盖了3次,也就是”已经考虑完了“

(这里并没有改变$f_{i,j}$的定义,只是让原本需要从这里一点点转移上去的部分直接转移)

特别的,第一个区间允许$j<i$,也允许选择多个区间,需要特判

时间复杂度即为$o(L^{2})$($L$为$l_{i},r_{i}$的范围),可以通过

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 4005
 4 #define mod 1000000007
 5 vector<int>v[N];
 6 int n,x,y,ans,f[N][N];
 7 void add(int &x,int y){
 8     x=(x+y)%mod;
 9 }
10 int main(){
11     scanf("%d",&n);
12     int m=0;
13     for(int i=1;i<=n;i++){
14         scanf("%d%d",&x,&y);
15         m=max(m,y);
16         v[x].push_back(y);
17     }
18     for(int i=1;i<=m;i++){
19         for(int j=0;j<=m;j++)add(f[i][j],f[i-1][j]);
20         sort(v[i].begin(),v[i].end());
21         for(int j=0;j<v[i].size();j++){
22             add(f[i][v[i][j]],1);
23             for(int k=j+1;k<v[i].size();k++)add(f[v[i][j]][v[i][k]],1);
24             for(int k=i;k<=m;k++)add(f[min(k,v[i][j])][max(k,v[i][j])],f[i-1][k]);
25         }
26     }
27     for(int i=0;i<=m;i++)add(ans,f[m][i]);
28     printf("%d",ans);
29 }
View Code

 

posted @ 2021-02-22 14:23  PYWBKTDA  阅读(164)  评论(0编辑  收藏  举报