题目描述
社交圈子里有 nnn 个人,每个人都有一个 SAN 值范围 [li,ri][l_i,r_i][li,ri]。当两个人的 SAN 值交集不为空时,这两个人有 PY 关系。
现在希望从社交圈子里面挑选出一些人组成一个集合 SSS,如果将所有集合内的人中有 PY 关系的那一对人都连上边,则 SSS 刚好成为一个树(森林不行哦)。
请问,有多少种可以选择的方案?由于答案可能很大,请对 109+710^{9}+7109+7 取模。
题解
- 我们首先将这些人的左端点从小到大排序,然后题目要求是要形成一棵树
- 这样的话,每一个点最多只能被两个区间覆盖,否则就会形成环
- 考虑dp怎么做,设f[i][j]表示我们从左到右选区间,倒数第二个区间为i,最后一个区间为j的方案数
- 我们转移的时候枚举j(r[j]<l[i])和k(r[k]<l[j]),就是i和j有交集,k和j有交集,i和k没有交集
- 动态转移方程为:f[i][j]+=f[j][k],f[j][i]+=f[j][k]
代码
1 #include <queue> 2 #include <cstdio> 3 #include <cstring> 4 #include <iostream> 5 #include <algorithm> 6 #define ll long long 7 using namespace std; 8 const ll N=2010,mo=1e9+7; 9 struct node { ll l,r; }q[N]; 10 struct Node 11 { 12 ll x,y; 13 bool operator < (const Node &a)const { return x>a.x; } 14 }; 15 priority_queue<Node>Q; 16 ll n,ans,bz[N],g[N],f[N][N]; 17 bool cmp(node x,node y) { return x.l==y.l?x.r>y.r:x.l<y.l; } 18 int main() 19 { 20 scanf("%lld",&n); 21 for (ll i=1;i<=n;i++) scanf("%lld%lld",&q[i].l,&q[i].r); 22 sort(q+1,q+n+1,cmp); 23 for (ll i=1;i<=n;i++) 24 { 25 while (!Q.empty()&&Q.top().x<q[i].l) 26 { 27 ll p=Q.top().y; bz[p]=0,Q.pop(); 28 for (ll j=1;j<=n;j++) if (bz[j]) (g[j]+=f[j][p])%=mo; 29 } 30 Q.push((Node){q[i].r,i}),bz[i]=1,f[i][i]=1; 31 for (ll j=1;j<=n;j++) 32 if (bz[j]&&i!=j) 33 { 34 if (q[j].r>q[i].r) f[j][i]+=1+g[j]; else f[i][j]+=1+g[j]; 35 ans=(ans+1+g[j])%mo; 36 } 37 } 38 printf("%lld",(ans+n)%mo); 39 }