嘘~ 正在从服务器偷取页面 . . .

洛谷P5464 缩小社交圈 题解


洛谷P5464 缩小社交圈 题解

题目链接:P5464 缩小社交圈

题意

社交圈子里有 $n$ 个人,每个人都有一个 SAN 值范围 $[l_i,r_i]$。当两个人的 SAN 值交集不为空时,这两个人有 PY 关系。

现在希望从社交圈子里面挑选出一些人组成一个集合 $S$,如果将所有集合内的人中有 PY 关系的那一对人都连上边,则 $S$ 刚好成为一个树(森林不行哦)。

请问,有多少种可以选择的方案?由于答案可能很大,请对 $10^{9}+7$ 取模。

输入格式

第一行一个整数 $n$。

接下来 $n$ 行,每行 2 个整数,表示这个人的 SAN 值区间。

输出格式

一行一个整数,表示答案。

数据范围

满足 $n \leq 2000,1 \leq l_{i} <r_{i} \leq 4000 $ 。

首先把所有线段按右端点升序排序,这样我们可以依次考虑每条线段。

注意到答案的树形如一条长链,其中一些节点挂了几个单点。

考虑设 $f(i,j)$ 表示编号最右侧的线段是 $i$ ,次右的是 $j$ 时的合法方案数。

记 $S(i,j) = \sum_{1 \le j < i} f(i,j)$ ,答案就是 $n + \sum S(i,i-1)$ 。

并记 $\operatorname{pre}(i)$ 为比 $i$ 编号小的线段中编号最大的不与 $i$ 相交的线段的编号。

转移暴力枚举 $i$ 左侧的线段 $j~(1 \le j < i)$ ,除了 $r_j < l_i$ 的线段外(不相交),

  1. 若 $l_j < l_i$ ,$j$ 可能和左侧的点连上,则
  1. 否则 $i$ 可能和左侧的点连上,有

最后更新一下 $S(i,j)=S(i,j-1) + f(i,j)$ ,这实际上是前缀和优化。

时间复杂度 $\mathcal{O}(n^2)$

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
const int mod = 1e9 + 7;
template<typename T> void up(T &x, T y) { x < y ? x = y : 0; }
template<typename T> void down(T &x, T y) { x > y ? x = y : 0; }
template<typename T> void add(T &x, T y = 0) { (x += y) >= mod ? x -= mod : 0; }
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define N ((int)(2e3 + 15))

struct node{ int l, r; } a[N];
int n, pre[N], sum[N][N], dp[N][N];
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n; rep(i, 1, n) cin >> a[i].l >> a[i].r;
    sort(a + 1, a + 1 + n, [](node x, node y) { return x.r < y.r; });
    rep(i, 1, n) Rep(j, i - 1, 1)
        if(a[j].r < a[i].l) { pre[i] = j; break; }
    rep(i, 1, n) rep(j, 1, i - 1)
    {
        if(a[j].r < a[i].l) { sum[i][j] = sum[i][j - 1]; continue; }
        if(a[i].l > a[j].l) { add(dp[i][j] = sum[j][pre[i]], 1ll); }
        else { add(dp[i][j] = sum[i][pre[j]], 1ll); }
        add(sum[i][j] = dp[i][j], sum[i][j - 1]);
    }
    int res = n % mod;
    rep(i, 1, n) add(res, sum[i][i - 1]);
    cout << res << '\n';
    return 0;
}

参考文献

[1] https://www.luogu.com.cn/article/mo7icl5r


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录