DTOJ#5202. 树的解构

传送门

Mivik 喜欢 Eprom 的解构俱乐部,于是他想解构一棵树。

Mivik 找到了一棵以 1 1 1 为根的有 n n n 个结点的有根外向树。Mivik 会进行 ( n − 1 ) (n − 1) (n1) 次操作,每次 Mivik 都会从未删掉的边中等概率选择一条边将其删去。记这条边为 a → b a → b ab,则删去这条边的代价是删边时 b b b 的子树大小(包括 b b b 自己);删去这条边后 b b b 为根的子树会形成一棵新的以 b b b 为根的有根树。

例如,下图是 Mivik 找到的有根树:

如果 Mivik 选了 1 → 2 1 \to 2 12 这条边并将其删去,那么代价是 3 3 3 2 2 2 所在的子树内共有 2 , 4 , 5 2,4,5 2,4,5 三个结点),而后情况会变成这样:

如果 Mivik 此时再删去 2 → 4 2 \to 4 24 这条边,那么代价是 1 1 1 4 4 4 所在的子树内只有 4 4 4 一个结点),
随后情况会变成这样:

Mivik 想知道,他进行这 ( n − 1 ) (n − 1) (n1) 次操作后期望的代价总和是多少。由于 Mivik 不喜欢太大的数,你只需要输出期望的值对 1 0 9 + 7 10^9 + 7 109+7 取模的结果。

输入共两行。

第一行一个正整数 n n n,表示给出的有根树的大小。

第二行给出 ( n − 1 ) (n − 1) (n1) 个正整数,第 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.indeconstruct/deconstruct3.ans

对于所有测试点,满足 1 ≤ n ≤ 2 × 1 0 6 1 \leq n \leq 2 \times 10^6 1n2×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 n500
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!(len1)!,求和即上式。
但蒟蒻学 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[j1]×Ci1j1×Aij)+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;
}

猜你喜欢

转载自blog.csdn.net/CSDNzhanghongyu/article/details/110496193