信息学奥赛一本通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;
}
}

浙公网安备 33010602011771号