[优先队列][dp] Luogu P5464 缩小社交圈

题目描述

社交圈子里有 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 }

 

posted @ 2019-08-06 15:34  BEYang_Z  阅读(209)  评论(0编辑  收藏  举报