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

洛谷P5464 缩小社交圈 题解


洛谷P5464 缩小社交圈 题解

题目链接:P5464 缩小社交圈

题意

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

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

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

输入格式

第一行一个整数 \(n\)

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

输出格式

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

数据范围

满足 $n ,1 l_{i} <r_{i} $ 。

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

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

考虑设 \(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\) 可能和左侧的点连上,则 \[ f(i,j) = S(j,\mathrm{pre}(i)) + 1 \]

  2. 否则 \(i\) 可能和左侧的点连上,有 \[ f(i,j) = S(i,\mathrm{pre}(j)) + 1 \]

最后更新一下 \(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 !
评论
  目录