AtCoder Grand Contest 046 F Forbidden Tournament

洛谷传送门

AtCoder 传送门

太厉害了!!!!!!

首先竞赛图有个性质,若存在环则一定存在三元环。

先把 DAG 的情况(一条链)特判了。然后缩点。发现非链底的部分不能存在大小 \(> 1\) 的 SCC。所以枚举非链底的部分有多少点,转化为 SCC 的情况。

发现对于任意点(设为 \(1\) 号点),它的前驱连成一条链,后继也是一条链。

如果前驱有环那么和 \(1\) 可以形成子图。如果后继最后有一个 SCC,考虑拿出前驱的链顶 \(y\)。显然这个 SCC 的点不能全部连向 \(y\)。所以这个 SCC 能分成两个非空集合 \(S_1, S_2\),满足一个连向 \(y\),一个被 \(y\) 连。发现 \(S_1\) 不能存在到 \(S_2\) 的边。所以这不是一个 SCC。

设前驱的链为 \(A_{1 \sim a}\),后继的链为 \(B_{1 \sim b}\)

首先因为这是一个 SCC 所以一定有 \(B_b \to A_1\) 的边。

然后我们发现对于前驱的点,它一定是有一段连向后继的前缀,剩下的后缀是连向这个点。

设前驱第 \(i\) 个点连向后继的是 \([1, p_i]\)。又发现 \(p\) 单调不降。

然后就可以转化为对于 \(p\) 计数。入度判一下即可。

时间复杂度 \(O(n^4)\)

code
// Problem: F - Forbidden Tournament
// Contest: AtCoder - AtCoder Grand Contest 046
// URL: https://atcoder.jp/contests/agc046/tasks/agc046_f
// Memory Limit: 1024 MB
// Time Limit: 4000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 210;

ll n, m, mod, fac[maxn], C[maxn][maxn], f[maxn][maxn];

inline ll calc(ll n, ll m) {
	ll ans = 0;
	for (int a = 1; a < n - 1; ++a) {
		int b = n - 1 - a;
		mems(f, 0);
		if (a > m || b > m) {
			continue;
		}
		for (int i = 0; i < b; ++i) {
			if (b - i <= m && (!i || a + i <= m)) {
				f[1][i] = 1;
			}
		}
		for (int i = 2; i <= a; ++i) {
			ll s = 0;
			for (int j = 0; j <= b; ++j) {
				s = (s + f[i - 1][j]) % mod;
				if (i - 1 + b - j <= m && (!j || j + a - i + 1 <= m)) {
					f[i][j] = s;
				}
			}
		}
		for (int i = 0; i <= b; ++i) {
			ans = (ans + fac[n - 1] * f[a][i]) % mod;
		}
	}
	return ans;
}

void solve() {
	scanf("%lld%lld%lld", &n, &m, &mod);
	fac[0] = 1;
	for (int i = 1; i <= n; ++i) {
		fac[i] = fac[i - 1] * i % mod;
	}
	for (int i = 0; i <= n; ++i) {
		C[i][0] = C[i][i] = 1;
		for (int j = 1; j < i; ++j) {
			C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
		}
	}
	ll ans = (m == n - 1 ? fac[n] : 0);
	for (int i = 0; i <= n - 3 && i < m; ++i) {
		ans = (ans + C[n][i] * fac[i] % mod * calc(n - i, m - i)) % mod;
	}
	printf("%lld\n", ans);
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

posted @ 2024-01-16 16:17  zltzlt  阅读(16)  评论(0)    收藏  举报