QOJ 7348. Counting Orders Sol
没看题解,也算做独立写出。(逃)
题目链接
问题转化一下,等价于这个数有多少种拓扑序,使得 \(k\) 的位置为 \(v\)。
想一下这道题的弱化:没有 \(k,v\) 的限制,只算有多少种拓扑序,怎么做?
这个结论还挺经典的:记 \(f_u\) 表示以 \(u\) 节点为根的子树,其拓扑序有多少种。记 \(siz_u\) 表示以 \(u\) 节点为根的子树大小。
则有:\(f_u = (siz_u-1)! / (\prod_{v\in u} (siz_v)!) \times \prod_{v \in u} f_v\) 。
如何理解?可以先把同棵子树内的点视作颜色相同的点。\((siz_u-1)!\) 表示进行乱排,但是同色的视作相同,因此有 \(/(\prod_{v\in u} (siz_v)!)\)。但是同色点内部实际上是有顺序的,因此又要 \(\prod_{v \in u} f_v\)。
因此,我们可以 \(O(n)\) 得出结果。
而在本题中,多了限制,但是时间也放宽到了 \(O(n^2)\)。因此,我们记 \(f_{i,j}\),用来表示 \(i\) 为根的子树,\(v\) 的位置在 \(j\) 的拓扑序的总数。特别的,如果子树内不存在 \(v\) ,则 \(j\) 为0。然后根据定义进行转移。
具体的,假设我们当前在转移 \(f_u\) 。预处理出不含有 \(v\) 的子树。对于含有 \(v\) 的子树,枚举 \(f_{v,j}\) 。然后尝试让其对 \(f_{u,i}\) 产生贡献。显然的,我们可以在前 \(i-1\) 个位置选 \(j-1\) 个,后面的位置拿来选剩下的。对于剩下的 \(siz_u - 1 - siz_v\) 个位置,就和上面一样转移就行了。实在不行,自己手推下,应该是容易的。
Code
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define File(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout)
#define LL long long
#define fi first
#define se second
const int N = 5005;
const LL mod = 1e9 + 7;
vector<int> G[N];
int n;
LL fac[N],inv[N];
int V,K;
LL qpow(LL a,LL b){
if(b == 0) return 1;
if(b == 1) return a;
LL re = qpow(a,b/2);
re *= re;
re %= mod;
if(b & 1ll) re *= a;
return re % mod;
}
LL C(LL a,LL b){
if(a < b) return 0;
return fac[a] * inv[b] % mod * inv[a-b] % mod;
}
LL f[N][N];
bool flag[N];
int siz[N];
void dfs(int u){
siz[u] = 1;
if(u == V) flag[u] = 1;
for(int v : G[u]){
dfs(v);
if(flag[v]) flag[u] = 1;
siz[u] += siz[v];
}
if(u == V){
LL ans = 1;
ans = fac[siz[u] - 1];
for(int v : G[u]){
ans = ans * inv[siz[v]] % mod * f[v][0] % mod;
}
f[u][1] = ans;
}
else if(flag[u] == 0){
LL ans = 1;
ans = fac[siz[u] - 1];
for(int v : G[u]){
ans = ans * inv[siz[v]] % mod * f[v][0] % mod;
}
f[u][0] = ans;
}
else{
LL k = 1;
LL nxt;
for(int v : G[u]){
if(flag[v]){nxt = v;continue;}
k = k * inv[siz[v]] % mod * f[v][0] % mod;
}
for(int i=1;i<=siz[nxt];i++){
if(f[nxt][i] == 0) continue;
for(int j=1+i;j<=siz[u];j++){
f[u][j] += C(j-2,i-1) * C(siz[u]-j,siz[nxt]-i) % mod * fac[siz[u]-1-siz[nxt]] % mod * k % mod * f[nxt][i] % mod;
f[u][j] %= mod;
}
}
}
}
int main()
{
IOS;
cin >> n;
fac[0] = 1;
for(int i=1;i<=n;i++)
fac[i] = fac[i-1] * 1ll * i % mod;
inv[n] = qpow(fac[n],mod-2);
for(int i=n-1;i>=0;i--)
inv[i] = inv[i+1] * 1ll * (i + 1) % mod;
for(int i=2;i<=n;i++){
int x;
cin >> x;
G[x].push_back(i);
}
cin >> V >> K;
dfs(1);
cout << f[1][K] << "\n";
return 0;
}
/*
1 2
2 3
*/

浙公网安备 33010602011771号