AC475B 2024省选联测26 排列

佚名 / 2024-03-01 / 原文

题意

对于所有满足 \(1 \le a < b \le n\)\((a, b)\) 的排列,需要满足:

  • 对于 \(1\le a < b < c \le n\)\((a, c)\) 处在 \((a, b)\)\((b, c)\) 之间。
  • 另外再给出 \(m\) 个限制,形如 \((a, b, c, d)\) 要求 \((a, b)\)\((c, d)\) 的前面。

Sol

其实这道题没有那么 hard。

首先先抛开 \(m\) 个限制。

trivial 地,考虑对于 \((a, b)\) 排列的每个区间,都满足包含 \((a, b)\)\((b, c)\) 也包含 \((a, c)\)

一眼传递闭包。

考虑将每个点对 \((a, b)\) 抽象为一条 \(a \to b\) 的边。

那么原来的排列,就被抽象为了一张完全图的加边过程。

这个过程满足什么呢?

满足对于当前时刻的任意位置,图和补图都有传递性。

我们发现这样的图只有 \(n!\) 个,到这里就已经做完了。

为什么呢?

我们把 \(n!\) 个排列拿出来,考虑对于逆序对连边,这样就恰好对应一个具有联通性的 DAG 了。

剩下简单计数,然后再判下 \(m\) 个条件即可。

Code

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <array>
#include <tuple>
#include <vector>
#define int long long
#define tupl tuple <int, int, int, int>
using namespace std;
#ifdef ONLINE_JUDGE

#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf[1 << 23], *p1 = buf, *p2 = buf, ubuf[1 << 23], *u = ubuf;

#endif
int read() {
	int p = 0, flg = 1;
	char c = getchar();
	while (c < '0' || c > '9') {
		if (c == '-') flg = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9') {
		p = p * 10 + c - '0';
		c = getchar();
	}
	return p * flg;
}
void write(int x) {
	if (x < 0) {
		x = -x;
		putchar('-');
	}
	if (x > 9) {
		write(x / 10);
	}
	putchar(x % 10 + '0');
}
const int N = 15, M = 4e6 + 5, mod = 998244353;

vector <tupl> qrl;

array <int, N> fac, idx, cur, isl;

array <int, M> f;

void Mod(int &x) {
	if (x >= mod) x -= mod;
	if (x < 0) x += mod;
}

signed main() {
	freopen("perm.in", "r", stdin);
	freopen("perm.out", "w", stdout);
	int n = read(), m = read();
	for (int i = 1; i <= m; i++) {
		tupl x;
		get <0>(x) = read(), get <1>(x) = read();
		get <2>(x) = read(), get <3>(x) = read();
		qrl.push_back(x);
	}
	fac[0] = 1;
	for (int i = 1; i <= n; i++)
		fac[i] = fac[i - 1] * i % mod, idx[i] = i;
	int T = 0;
	f[1] = 1;
	do {
		cur.fill(0); T++;
		for (int i = 1; i <= n; i++) {
			isl[idx[i]] = i;
			for (int j = i + 1; j <= n; j++)
				if (idx[j] < idx[i])
					cur[i]++;
		}
		bool flg = 0;
		for (auto k : qrl)
			if (isl[get <0>(k)] > isl[get <1>(k)] &&
				isl[get <2>(k)] < isl[get <3>(k)])
				flg = 1;
		if (flg) continue;
		for (int i = 1; i < n; i++) {
			if (idx[i] > idx[i + 1]) continue;
			int tp0 = T - cur[i] * fac[n - i]
				- cur[i + 1] * fac[n - i - 1]
				+ (cur[i + 1] + 1) * fac[n - i]
				+ cur[i] * fac[n - i - 1];
			f[tp0] += f[T], Mod(f[tp0]);
		}
	} while (next_permutation(idx.begin() + 1, idx.begin() + 1 + n));
	write(f[fac[n]]), puts("");
	return 0;
}