题解:P16175 [ICPC 2014 NAIPC] Cheats
lailai0916 · · 题解
题意简述
每个目标除根目标外都有一个前置目标。作弊可以让某个目标先于其父亲完成,但仍要晚于祖父;每个目标至多参与一次作弊。求使用不超过
解题思路
前置关系构成一棵以
每个节点至多参与一次作弊,等价于所选边构成树上的匹配。
考虑选择边
这等价于对树进行一次旋转。让
新树的拓扑序与恰好使用这些作弊的完成顺序一一对应。对根树,设节点
选择边
因此,这次旋转使拓扑序数量乘上:
先计算原树的拓扑序数量
设
依次合并儿子
所有除法均使用模意义下的逆元。时间复杂度为
参考代码
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=205;
const int mod=1000000007;
vector<int> g[N];
int siz[N];
ll f[N][N][2];
int lim;
ll Pow(ll x,ll y)
{
x%=mod;
ll res=1;
while(y)
{
if(y&1)res=res*x%mod;
x=x*x%mod;
y>>=1;
}
return res;
}
void dfs(int u)
{
siz[u]=1;
for(int v:g[u])
{
dfs(v);
siz[u]+=siz[v];
}
f[u][0][0]=1;
for(int v:g[u])
{
ll nf[N][2]={};
ll w=(ll)siz[v]*Pow(siz[u]-siz[v],mod-2)%mod;
for(int i=0;i<=lim;i++)
{
for(int j=0;i+j<=lim;j++)
{
ll sum=(f[v][j][0]+f[v][j][1])%mod;
for(int k=0;k<2;k++)nf[i+j][k]=(nf[i+j][k]+f[u][i][k]*sum)%mod;
if(i+j<lim)nf[i+j+1][1]=(nf[i+j+1][1]+f[u][i][0]*f[v][j][0]%mod*w)%mod;
}
}
for(int i=0;i<=lim;i++)
{
f[u][i][0]=nf[i][0];
f[u][i][1]=nf[i][1];
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
while(cin>>n>>lim,n||lim)
{
for(int i=1;i<=n;i++)g[i].clear();
memset(f,0,sizeof(f));
for(int i=2;i<=n;i++)
{
int p;
cin>>p;
g[p].push_back(i);
}
dfs(1);
ll base=1;
for(int i=1;i<=n;i++)
{
base=base*i%mod;
base=base*Pow(siz[i],mod-2)%mod;
}
ll ans=0;
for(int i=0;i<=lim;i++)ans=(ans+f[1][i][0]+f[1][i][1])%mod;
cout<<base*ans%mod<<'\n';
}
return 0;
}