[题目链接]
https://atcoder.jp/contests/agc028/tasks/agc028_d
[题解]
首先破环为链。
考虑两条线段在同一个联通块中需要满足什么条件?显然 , 这两条线段在链上对应的两个区间相交且互不包含。
接着考虑计算每个联通块的贡献 , 贡献之和即为答案。
一个联通块在数轴上对应的显然是一个区间 , 并且区间端点也属于该联通块。当然大联通块内可能会有小联通块的存在。
不妨定义 (dp(i , j)) 表示 ([i , j]) 所对应的区间任意连边 , 且 (i , j) 在同一联通块的方案数。
枚举这个区间 , 显然其长度必为偶数。
考虑容斥 , 用任意连方案数减去不合法的方案数。
令 (g(x)) 表示 (x) 个点任意连的方案数 , 有 (g(x) = 1 * 3 * 5 * ... * x)。
因此总数就是 (g(c(i , j))) , 其中 (c(i , j)) 表示 ([i , j]) 内未确定的边数。
对于不合法的方案数 , 不妨枚举 (i) 联通块的右端点 (k)。
故 (dp(i , j) = g(c(i , j)) - sum_{i = 1}^{j - 1}{dp(i , k) cdot g(c(k + 1 , j))})。
求出所有 (dp(i , j)) 后 , 就有答案为 (sum{dp_{i , j} cdot g(n - 2K - c(i , j))})。
时间复杂度 : (O(N ^ 3))
[代码]
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MN = 605 , mod = 1e9 + 7;
int N , K , G[MN] , J[MN] , F[MN][MN];
inline void inc(int &x , int y) {
x = x + y < mod ? x + y : x + y - mod;
}
inline void dec(int &x , int y) {
x = x - y >= 0 ? x - y : x - y + mod;
}
int main() {
scanf("%d%d" , &N , &K); N <<= 1;
G[0] = 1;
for (int i = 2; i <= N; i += 2) G[i] = 1ll * G[i - 2] * (i - 1) % mod;
for (int i = 1; i <= K; ++i) {
int x , y; scanf("%d%d" , &x , &y);
J[x] = y , J[y] = x;
}
int ans = 0;
for (int i = 1; i <= N; ++i) {
for (int j = i + 1; j <= N; j += 2) {
int ok = 1 , c = j - i + 1;
for (int k = i; k <= j; ++k) if (J[k] && (--c , J[k] < i || J[k] > j)) ok = 0;
if (!ok) continue;
F[i][j] = G[c];
for (int k = j , d = 0; k > i; --k)
d += !J[k] , dec(F[i][j] , 1ll * F[i][k - 1] * G[d] % mod);
inc(ans , 1ll * F[i][j] * G[N - 2 * K - c] % mod);
}
}
printf("%d
" , ans);
return 0;
}