[AGC028D] Chords

[题目链接]

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\n" , ans);
	return 0;  
}
posted @ 2021-02-01 22:29  evenbao  阅读(104)  评论(0编辑  收藏  举报