AT4438 [AGC028D] Chords 题解

该博客介绍了如何使用动态规划解决一类与区间交集相关的图论问题。通过将环形结构转换为线性链,将连通块表示为区间,并分析连通块间的关系,博主提出了计算方案数的方法。通过预处理gi和cnti,j数组,计算每个连通块对答案的贡献,并利用容斥原理得到最终答案。算法复杂度为O(n^2),并在文末提供了代码实现。" 111959252,10538131,video标签播放mp4问题排查与解决,"['前端开发', 'HTML', 'JavaScript', '视频播放', '浏览器兼容性']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

可以发现,如果把环断开成为一条链,那么线段的交实际上就类似于区间的交。

所以,我们可以考虑用连通块 S S S 中涉及到的编号最小的点 L L L 和编号最大的点 R R R 所构成的区间 [ L , R ] [L, R] [L,R] 来表示 S S S

下文我们称 [ L , R ] [L, R] [L,R] 为连通块 S S S表示区间


那么对于一个已经连好边的图,我们可以发现一个性质:

  • 对于两个不同的连通块,它们的表示区间要么是包含关系,要么交集为空。

这个性质是有利于统计的,因为我们不必考虑方案中产生的交区间对不同连通块的影响了。

具体地,我们可以考虑设 f i , j f_{i, j} fi,j 为构造出表示区间是 [ i , j ] [i, j] [i,j] 的连通块的方案数,那么这个连通块对答案的贡献是: f i , j f_{i, j} fi,j 和在区间 [ i , j ] [i, j] [i,j] 外的其它点任意连接的方案数的乘积。

这个“在区间 [ i , j ] [i, j] [i,j] 外的其它点任意连接的方案数”该怎么计算呢?

我们的方法是,预处理两个数组: g i g_i gi c n t i , j cnt_{i, j} cnti,j

g i g_i gi 表示 i i i 个点之间任意连接的方案数;

c n t i , j cnt_{i, j} cnti,j 表示区间 [ i , j ] [i, j] [i,j]没有连接的点的个数,即没有在给定的 k k k 条线段中出现过。

那么,“在区间 [ i , j ] [i, j] [i,j] 外的其它点任意连接的方案数”即为:

g ( n − k ) ∗ 2 − c n t i , j g_{(n - k) * 2 - cnt_{i, j}} g(nk)2cnti,j


接下来我们考虑怎样预处理这两个数组。

g i g_i gi 数组:当 i i i 为奇数时,结果为 0 0 0(奇数个点无法完整地相连),否则为 g i − 2 ∗ ( i − 1 ) g_{i - 2} * (i - 1) gi2(i1)

我们设 g 0 g_0 g0 1 1 1,那么 g g g 的通项公式平凡,这里就留给读者了,时间复杂度 O ( n ) O(n) O(n)

c n t i , j cnt_{i, j} cnti,j 数组:这个就不用多说了, O ( n 2 ) O(n^2) O(n2) 处理即可。


最后我们考虑如何计算 f i , j f_{i, j} fi,j,不过前提是要保证 f i , j f_{i, j} fi,j存在性和独立性

首先我们需要保证区间 [ i , j ] [i, j] [i,j] 中的所有连边不超出区间的范围,这样才能保证 f i , j f_{i, j} fi,j 的存在性,如果上述条件无法满足,那么令 f i , j = 0 f_{i, j} = 0 fi,j=0

如果不容斥,那么 f i , j f_{i, j} fi,j 就是 g c n t i , j g_{cnt_{i, j}} gcnti,j,但是显然需要容斥,因为区间 [ i , j ] [i, j] [i,j] 内部可能会出现不连通的情况

我们考虑枚举一个 k ∈ [ i + 1 , j − 1 ] k \in [i + 1, j - 1] k[i+1,j1],令区间 [ i , k ] [i, k] [i,k] [ k + 1 , j ] [k + 1, j] [k+1,j] 独立,由于在算 f i , k f_{i, k} fi,k 时已经保证了其独立性,所以这样不合法的方案有 f i , k ∗ g c n t k + 1 , j f_{i, k} * g_{cnt_{k + 1, j}} fi,kgcntk+1,j 种。

所以 f i , j f_{i, j} fi,j 的计算公式为:

f i , j = g c n t i , j − ∑ k = i + 1 j − 1 f i , k ∗ g c n t k + 1 , j f_{i, j} = g_{cnt_{i, j}} - \sum_{k = i + 1}^{j - 1}f_{i, k} * g_{cnt_{k + 1, j}} fi,j=gcnti,jk=i+1j1fi,kgcntk+1,j

算法到这里就大功告成了,实现的细节具体看代码吧。

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cassert>

#define FILE

#define MAXN 1005
#define MOD 1000000007

using namespace std;

int n, k, Match[MAXN], cnt[MAXN][MAXN], g[MAXN], f[MAXN][MAXN], ans;
bool Matched[MAXN];

int main()
{
    #ifdef FILE
        freopen("Input.in", "r", stdin);
        freopen("Output.out", "w", stdout);
    #endif
    scanf("%d%d", &n, &k);
    for(register int i = 1; i <= k; i++)
    {
        int u, v; scanf("%d%d", &u, &v);
        Matched[u] = Matched[v] = true;
        Match[u] = v, Match[v] = u;
    }
    for(register int i = 1; i <= n * 2; i++) for(register int j = i; j <= n * 2; j++) cnt[i][j] = cnt[i][j - 1] + !Matched[j];
    g[0] = 1; for(register int i = 2; i <= n * 2; i += 2) g[i] = 1LL * g[i - 2] * (i - 1) % MOD;
    for(register int i = 1; i <= n * 2; i++)
    {
        for(register int j = i; j <= n * 2; j++)
        {
            bool Flag = false;
            for(register int k = i; k <= j; k++) Flag |= (Matched[k] && (Match[k] < i || Match[k] > j));
            if(Flag) continue;
            f[i][j] = g[cnt[i][j]];
            for(register int k = i + 1; k <= j - 1; k++) f[i][j] = (f[i][j] - 1LL * f[i][k] * g[cnt[k + 1][j]] % MOD + MOD) % MOD;
        }
    }
    for(register int i = 1; i <= n * 2; i++) for(register int j = i; j <= n * 2; j++) ans = (ans + 1LL * f[i][j] * g[(n - k) * 2 - cnt[i][j]] ) % MOD;
    printf("%d\n", ans);
    return 0;
}

E N D \mathit{END} END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值