信息学奥赛一本通1772:动漫排序

原题链接:https://ybt.ssoier.cn/problem_show.php?pid=1772
【题目描述】
小W最近迷上了日本动漫,每天都有无数部动漫的更新等着他去看,所以他必须将所有的动漫排个顺序,当然,虽然有无数部动漫,但除了1
号动漫,每部动漫都有且仅有一部动漫是它的前传(父亲),也就是说,所有的动漫形成一个树形结构。而动漫的顺序必须满足以下两个限制:

①一部动漫的所有后继(子孙)都必须排在它的后面。

②对于同一部动漫的续集(孩子),小W喜爱度高的须排在前面。

光排序小W还不爽,他想知道一共有多少种排序方案,并且输出它mod10007
的答案。

对于100%的数据,n≤1000

首先这题肯定是一个树形dp+排列组合的题目,那我们设dp[u]为以u为根的子树的方案数

//下面的C是组合数,不知道组合数的出门右转,siz[u]是子树u的大小

然后我们考虑如何转移,首先对于一棵树,他第一个位置肯定是填根节点,然后再填小W第一喜欢的那个节点,所以前两个位置是固定的,然后第三个位置开始要么是选第二喜欢的节点,要么是选第一喜欢节点的子树上的点。我们先考虑填第一喜欢的字树上的点,在不考虑顺序的情况下,他们点的分布有C(siz[u]-2,siz[v1]-1)种分布情况,但他们内部还有顺序,所以要*dp[v1],这就是第一个节点的分布。好,假设我们第一个节点及其子树已经分好了,然后考虑第二个节点,他一定会在最左边的空白位置。

为什么?因为如果说他不在这里,那么这个位置一定不会被填,我们想,他前面的第一个点和他子树已经填完了,他自己的子树一定不能再他前面,剩下第三第四节点和他们的子树也要在他后面,所以说这个点的位置也是定死的。
那么我们就可以把第一个点的思路套到第二个点上面,现在还剩siz[u]-2-siz[v1]个空白,有siz[v2]-1个数要分布,那么第二个就是
C(siz[u]-2-siz[v1],siz[v2]-1) * dp[v2]

那么我们就可以得到转移方程了:dp[u] = C(siz[u]-2,siz[v1]-1) * C(siz[u]-2-siz[v1],siz[v2])...*∏dp[vi]
(我太菜了不知道怎么用数学的方法写,能理解就行)

初始化:dp[叶子结点] = 1
复杂度O(n)

组合数的部分不用我多说,我逆元直接懒得递推了,用费马小定理求的,直接看代码吧

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int N = 1001;
const int mod = 10007;

ll fast_pow(ll a, ll b) {
	ll ret = 1;
	while (b) {
		if (b & 1) {
			ret = ret * a % mod;
		}
		a = a * a % mod;
		b >>= 1;
	}
	return ret;
}

ll inv(ll x) { return fast_pow(x, mod - 2); }

int n, siz[N];
vector<int>G[N];
ll fact[N + 1], inv_fact[N + 1];
ll dp[N];

void init() {
	fact[0] = 1;
	for (int i = 1;i <= N;i++)fact[i] = fact[i - 1] * i % mod;
	for (int i = 0;i <= N;i++)inv_fact[i] = inv(fact[i]);
}

ll C(ll a, ll b) {
	return fact[a] * inv_fact[b] % mod * inv_fact[a - b] % mod;
}

void dfs(int u) {
	dp[u] = 1, siz[u] = 1;
	for (int v : G[u]) {
		dfs(v);
		siz[u] += siz[v];
		dp[u] = dp[u] * dp[v] % mod;
	}
	int temp = 0;
	for (int v : G[u]) {
		dp[u] = dp[u] * C(siz[u] - 2 - temp, siz[v] - 1) % mod;
		temp += siz[v];
	}
}

int main() {
	int T;
	init();
	cin >> T;
	while (T--) {
		cin >> n;
		for (int i = 1;i <= n;i++) {
			G[i].clear();
			int tot, j;
			cin >> tot;
			while (tot--) {
				cin >> j;
				G[i].push_back(j);
			}
		}
		dfs(1);
		cout << dp[1] << endl;
	}
}
posted @ 2026-08-19 18:00  DK_orz  阅读(5)  评论(0)    收藏  举报