( ext{Description})
( ext{Solution})
他们通过无线通讯设备确定一个集合点,然后所有人沿最短路径走到集合点,花费的代价为所有人走的路径长度之和。 为了取得竞技的胜利,他们需要最小化花费的代价。
其实如果是指定节点/任意节点最小化花费应该都是很好做的换根 ( ext{DP})。
然而他们并不知道他们最终会进入哪个节点,所以你需要对于每种不同的情况计算最小代价并求出总和。
这个就很艹了,如果用普通的 (f[i][j]) 定义(枚举集合点)并不好做:算总方案数就没法保证最小化,算 (min) 就没法算总方案。
头大。
正解是对于每条线段来算贡献。考虑一条边两端关键点个数分别为 (x,m-x),如果有 (xle m-x),肯定会把集合点设在 (m-x) 的那个连通块里,这样它只会被经过 (x) 次。
假设一条边两侧分别有 (s,n-s) 个点,那么这条边的贡献就是:
这样肯定过不去,考虑优化掉 (min),其实如果强制 (xle m-x) 就有 (xle lfloorfrac{m}{2} floor)。考虑 (x) 范围只有 ([1,m-1]),把偶数的 (frac{m}{2}) 情况刨开,就有,
那么我们只用快速求(前面两项差不多):
到这里就有两种思路:将求和去掉或预处理后 (mathcal O(1)) 递推。
可是我数学差只会看题解,题解写什么就是什么 (qwq)
设 (lfloorfrac{m-1}{2} floor=k),就有:
并令其为 (s imes g(s))。
这个柿子有组合意义(就是排列组合的经典模型):在 (n-1) 个球里选 (m-1) 个球,保证前 (s-1) 中最多选 (k-1) 的方案数。显然 (g(s+1)) 相对于 (g(s)) 只少了在前 (s-1) 选了 (k-1) 个球,又选了第 (s) 个球的情况(考虑限制更紧,肯定会变少),这种方案数可以表示为 ( ext C(s-1,k-1) imes ext C(n-1-s,m-1-k)):在前 (s-1) 选了 (k-1) 个球,(n-1-s) 表示除了前 (s) 个球的球数,(m-1-k) 表示前面 (s) 个球选了 (k) 个,那么后面 (n-1-s) 个球要选 (m-1-k) 个,显然这样强制了选了第 (s) 个球。
( ext{Code})
#include <cstdio>
#define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
#define fep(i,_l,_r) for(register signed i=(_l),_end=(_r);i>=_end;--i)
#define erep(i,u) for(signed i=head[u],v=to[i];i;i=nxt[i],v=to[i])
#define efep(i,u) for(signed i=Head[u],v=to[i];i;i=nxt[i],v=to[i])
#define print(x,y) write(x),putchar(y)
template <class T> inline T read(const T sample) {
T x=0; int f=1; char s;
while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
return x*f;
}
template <class T> inline void write(const T x) {
if(x<0) return (void) (putchar('-'),write(-x));
if(x>9) write(x/10);
putchar(x%10^48);
}
template <class T> inline T Max(const T x,const T y) {if(x>y) return x; return y;}
template <class T> inline T Min(const T x,const T y) {if(x<y) return x; return y;}
template <class T> inline T fab(const T x) {return x>0?x:-x;}
template <class T> inline T gcd(const T x,const T y) {return y?gcd(y,x%y):x;}
template <class T> inline T lcm(const T x,const T y) {return x/gcd(x,y)*y;}
template <class T> inline T Swap(T &x,T &y) {x^=y^=x^=y;}
const int mod=1e9+7,maxn=1e6+5;
int n,m,fac[maxn],ifac[maxn],k,siz[maxn],ans,g[maxn];
int head[maxn],to[maxn<<1],nxt[maxn<<1],cnt;
void addEdge(int u,int v) {
nxt[++cnt]=head[u],to[cnt]=v,head[u]=cnt;
nxt[++cnt]=head[v],to[cnt]=u,head[v]=cnt;
}
int qkpow(int x,int y) {
int r=1;
while(y) {
if(y&1) r=1ll*r*x%mod;
x=1ll*x*x%mod; y>>=1;
}
return r;
}
void init() {
fac[0]=1;
rep(i,1,n) fac[i]=1ll*fac[i-1]*i%mod;
ifac[n]=qkpow(fac[n],mod-2);
fep(i,n-1,0) ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
}
int C(int x,int y) {
if(x<y) return 0;
return 1ll*fac[x]*ifac[y]%mod*ifac[x-y]%mod;
}
void dfs(int u,int fa) {
siz[u]=1;
erep(i,u) {
if(v==fa) continue;
dfs(v,u);
siz[u]+=siz[v];
ans=((ans+g[siz[v]])%mod+g[n-siz[v]])%mod;
if(!(m&1)) ans=(ans+1ll*C(siz[v],m>>1)*C(n-siz[v],m>>1)%mod*(m>>1)%mod)%mod;
}
}
int main() {
// freopen("meeting.in", "r", stdin);
// freopen("meeting.out", "w", stdout);
int x;
n=read(9),m=read(9); k=m-1>>1; init();
rep(i,2,n) x=read(9),addEdge(x,i);
if(k>=1) g[1]=C(n-1,m-1); // 注意特判这个时候没有数值
rep(i,1,n-1) g[i+1]=(g[i]-1ll*C(i-1,k-1)*C(n-1-i,m-1-k)%mod+mod)%mod;
rep(i,1,n) g[i]=1ll*g[i]*i%mod;
dfs(1,0);
print(ans,'
');
return 0;
}