可以发现,如果把环断开成为一条链,那么线段的交实际上就类似于区间的交。
所以,我们可以考虑用连通块 SSS 中涉及到的编号最小的点 LLL 和编号最大的点 RRR 所构成的区间 [L,R][L, R][L,R] 来表示 SSS。
下文我们称 [L,R][L, R][L,R] 为连通块 SSS 的表示区间。
那么对于一个已经连好边的图,我们可以发现一个性质:
- 对于两个不同的连通块,它们的表示区间要么是包含关系,要么交集为空。
这个性质是有利于统计的,因为我们不必考虑方案中产生的交区间对不同连通块的影响了。
具体地,我们可以考虑设 fi,jf_{i, j}fi,j 为构造出表示区间是 [i,j][i, j][i,j] 的连通块的方案数,那么这个连通块对答案的贡献是:fi,jf_{i, j}fi,j 和在区间 [i,j][i, j][i,j] 外的其它点任意连接的方案数的乘积。
这个“在区间 [i,j][i, j][i,j] 外的其它点任意连接的方案数”该怎么计算呢?
我们的方法是,预处理两个数组:gig_igi 和 cnti,jcnt_{i, j}cnti,j:
gig_igi 表示 iii 个点之间任意连接的方案数;
cnti,jcnt_{i, j}cnti,j 表示区间 [i,j][i, j][i,j] 内没有连接的点的个数,即没有在给定的 kkk 条线段中出现过。
那么,“在区间 [i,j][i, j][i,j] 外的其它点任意连接的方案数”即为:
g(n−k)∗2−cnti,j g_{(n - k) * 2 - cnt_{i, j}} g(n−k)∗2−cnti,j
接下来我们考虑怎样预处理这两个数组。
gig_igi 数组:当 iii 为奇数时,结果为 000(奇数个点无法完整地相连),否则为 gi−2∗(i−1)g_{i - 2} * (i - 1)gi−2∗(i−1)。
我们设 g0g_0g0 为 111,那么 ggg 的通项公式平凡,这里就留给读者了,时间复杂度 O(n)O(n)O(n) 。
cnti,jcnt_{i, j}cnti,j 数组:这个就不用多说了,O(n2)O(n^2)O(n2) 处理即可。
最后我们考虑如何计算 fi,jf_{i, j}fi,j,不过前提是要保证 fi,jf_{i, j}fi,j 的存在性和独立性。
首先我们需要保证区间 [i,j][i, j][i,j] 中的所有连边不超出区间的范围,这样才能保证 fi,jf_{i, j}fi,j 的存在性,如果上述条件无法满足,那么令 fi,j=0f_{i, j} = 0fi,j=0 。
如果不容斥,那么 fi,jf_{i, j}fi,j 就是 gcnti,jg_{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,j−1],令区间 [i,k][i, k][i,k] 和 [k+1,j][k + 1, j][k+1,j] 独立,由于在算 fi,kf_{i, k}fi,k 时已经保证了其独立性,所以这样不合法的方案有 fi,k∗gcntk+1,jf_{i, k} * g_{cnt_{k + 1, j}}fi,k∗gcntk+1,j 种。
所以 fi,jf_{i, j}fi,j 的计算公式为:
fi,j=gcnti,j−∑k=i+1j−1fi,k∗gcntk+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,j−k=i+1∑j−1fi,k∗gcntk+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;
}
该博客介绍了如何使用动态规划解决一类与区间交集相关的图论问题。通过将环形结构转换为线性链,将连通块表示为区间,并分析连通块间的关系,博主提出了计算方案数的方法。通过预处理gi和cnti,j数组,计算每个连通块对答案的贡献,并利用容斥原理得到最终答案。算法复杂度为O(n^2),并在文末提供了代码实现。"
111959252,10538131,video标签播放mp4问题排查与解决,"['前端开发', 'HTML', 'JavaScript', '视频播放', '浏览器兼容性']
746

被折叠的 条评论
为什么被折叠?



