Mivik
喜欢 Eprom
的解构俱乐部,于是他想解构一棵树。
Mivik
找到了一棵以 1 1 1 为根的有 n n n 个结点的有根外向树。Mivik
会进行 ( n − 1 ) (n − 1) (n−1) 次操作,每次 Mivik
都会从未删掉的边中等概率选择一条边将其删去。记这条边为 a → b a → b a→b,则删去这条边的代价是删边时 b b b 的子树大小(包括 b b b 自己);删去这条边后 b b b 为根的子树会形成一棵新的以 b b b 为根的有根树。
例如,下图是 Mivik
找到的有根树:
如果 Mivik
选了 1 → 2 1 \to 2 1→2 这条边并将其删去,那么代价是 3 3 3( 2 2 2 所在的子树内共有 2 , 4 , 5 2,4,5 2,4,5 三个结点),而后情况会变成这样:
如果 Mivik
此时再删去 2 → 4 2 \to 4 2→4 这条边,那么代价是 1 1 1( 4 4 4 所在的子树内只有 4 4 4 一个结点),
随后情况会变成这样:
Mivik
想知道,他进行这 ( n − 1 ) (n − 1) (n−1) 次操作后期望的代价总和是多少。由于 Mivik
不喜欢太大的数,你只需要输出期望的值对 1 0 9 + 7 10^9 + 7 109+7 取模的结果。
输入共两行。
第一行一个正整数 n n n,表示给出的有根树的大小。
第二行给出 ( n − 1 ) (n − 1) (n−1) 个正整数,第 i i i 个数 a i a_i ai 表示 ( i + 1 ) (i + 1) (i+1) 号结点在有根树上的父亲是 a i a_i ai。
输出一行一个非负整数,表示代价的期望对 1 0 9 + 7 10^9 + 7 109+7 取模的结果。
样例输入1
4
1 2 2
样例输出1
4
样例解释1
解构的总代价有 1 / 3 1/3 1/3 的概率为 3 3 3,有 1 / 3 1/3 1/3 的概率为 4 4 4,有 1 / 3 1/3 1/3 的概率为 5 5 5,所以代价的期望为 4 4 4。
样例输入2
5
1 1 2 2
样例输出2
5
样例3
见选手目录下的 deconstruct/deconstruct3.in
与 deconstruct/deconstruct3.ans
。
对于所有测试点,满足 1 ≤ n ≤ 2 × 1 0 6 1 \leq n \leq 2 \times 10^6 1≤n≤2×106。保证给出的有根树合法。
每个子任务的具体限制见下表:
子任务编号 | 分值 | 特殊限制 |
---|---|---|
1 1 1 | 10 10 10 | a i = 1 a_i=1 ai=1 |
2 2 2 | 15 15 15 | a i = i a_i=i ai=i |
3 3 3 | 25 25 25 | n ≤ 500 n \leq 500 n≤500 |
4 4 4 | 50 50 50 | 无 |
成都七中 noip2020模拟
考虑计算每个点的答案。
据说有神犇瞪眼看出规律为 点 i i i 的答案是 ∑ j d e p [ i ] 1 j \sum_j^{dep[i]} \frac{1}{j} ∑jdep[i]j1,直接求和即可。
u p d a t e : update: update:据说是对于根到 i i i 的链,考虑每条边贡献的概率即 ( l e n − 1 ) ! l e n ! \frac{(len-1)!}{len!} len!(len−1)!,求和即上式。
但蒟蒻学 D P DP DP 学傻了。于是我们考虑预处理 f [ i ] f[i] f[i] 表示长为 i i i 的链末端的答案。
转移即枚举 i i i 的位置 f [ i ] = ( ∑ f [ j − 1 ] × C i − 1 j − 1 × A i − j ) + A i f[i]=(\sum f[j-1]\times C_{i-1}^{j-1}\times A_{i-j})+A_{i} f[i]=(∑f[j−1]×Ci−1j−1×Ai−j)+Ai
展开化简加前缀和优化即可 O ( n ) O(n) O(n)处理。
#include<bits/stdc++.h>
#define N 2000006
using namespace std;
inline char GET_CHAR ( void )
{
static char buf[1<<23],*p1=buf,*p2=buf;
return p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<23,stdin),p1==p2) ? EOF : *p1++;
}
inline int read ( void )
{
int x=0;char ch;
while ( !isdigit(ch=GET_CHAR()) ) ;
for ( x=ch^48;isdigit(ch=GET_CHAR()); ) x=(x<<1)+(x<<3)+(ch^48);
return x;
}
const int mod=1e9+7;
int jc[N],jcinv[N],f[N];
inline int power(int x,int c){
int now=1;
while(c){
if(c&1)now=1ll*now*x%mod;
x=1ll*x*x%mod;c>>=1;
}
return now;
}
inline void init(int n){
jc[0]=1;
for(int i=1;i<=n;++i)jc[i]=1ll*jc[i-1]*i%mod;
jcinv[n]=power(jc[n],mod-2);
for(int i=n-1;i+1;--i)jcinv[i]=1ll*jcinv[i+1]*(i+1)%mod;
int sum=0;
for(int i=1;i<=n;++i){
f[i]=sum%mod;
f[i]=(1ll*f[i]*jc[i-1]%mod+jc[i])%mod;
sum=(sum+1ll*f[i]*jcinv[i]%mod)%mod;
}
}
inline int C(int x,int y){
if(x<y||y<0)return 0;
return 1ll*jc[x]*jcinv[y]%mod*jcinv[x-y]%mod;
}
int ans=0,dep[N],n;
int tot,head[N],ver[N<<1],nex[N<<1];
inline void add(int x,int y){
nex[++tot]=head[x];head[x]=tot;ver[tot]=y;
}
void dfs(int x,int las){
ans=(ans+1ll*f[dep[x]]*C(n-1,dep[x])%mod*jc[n-1-dep[x]]%mod)%mod;
for(int i=head[x];i;i=nex[i]){
int y=ver[i];
if(y==las)continue;
dep[y]=dep[x]+1;
dfs(y,x);
}
}
int main(){
// freopen("deconstruct.in","r",stdin);
// freopen("deconstruct.out","w",stdout);
n=read();
init(n+1);
for(int i=2;i<=n;++i){
int x=read();
add(x,i);add(i,x);
}
dfs(1,0);
ans=1ll*ans*jcinv[n-1]%mod;
printf("%d\n",(ans+mod)%mod);
return 0;
}