题目

Description
【JZOJ 省选模拟】欢迎来到塞莱斯特山_经验分享

Input
【JZOJ 省选模拟】欢迎来到塞莱斯特山_dp_02

Output
一行一个整数表示题意描述中的式子的值。

Sample Input
样例 1输入:
3
1 2
样例 2输入:
5
1 2 3 2
​样例 3输入:
9
1 2 1 2 5 4 7 8

Sample Output
样例 1输出:
10
样例 2输出:
816
样例 3输出:
6580992

Data Constraint

【JZOJ 省选模拟】欢迎来到塞莱斯特山_dp_03
Hint
【JZOJ 省选模拟】欢迎来到塞莱斯特山_dp_04

思路

题目要求lca的dep的乘积和,考虑把这个dep摊到lca的每一个祖先上。
设f[i][j]表示i为根的子树里,在下面合并完后,还剩j段的方案数。该状态段与段之间的顺序没有确
定。
f的转移:
每一次先把子树的段继承过来(子树背包实现),最后在这个点上合并段,合并段的系数可以预处理一个g[i][j] 表示i 段合并成j段的方案数,由 g[i]可以递推出g[i+1]。

代码

#include<bits/stdc++.h>
#define maxn 505
#define ll long long 
#define mo 1000000007
using namespace std;

int n,fa[maxn],sz[maxn];
int em,e[maxn],nx[maxn],ls[maxn];
ll mul[maxn][maxn],f[maxn][maxn],g[maxn];
ll h[maxn][maxn][maxn],s[maxn*2];

void ins(int x,int y){
	em++; e[em]=y; nx[em]=ls[x]; ls[x]=em;
}

void dfs(int x,int dep){
	for(int i=ls[x];i;i=nx[i]) dfs(e[i],dep+1);
	sz[x]=1,f[x][1]=1;
	for(int i=ls[x];i;i=nx[i]) { 
		int y=e[i];
		memset(g,0,sizeof(g));
		for(int j=1;j<=sz[x];j++) for(int k=1;k<=sz[y];k++)	for(int l=max(1,j-k);l<=j+k;l++)
			g[l]+=h[j][k][l]%mo*f[x][j]%mo*f[y][k]%mo*mul[dep][k-(l-j)]%mo;
		sz[x]+=sz[y];
		for(int j=1;j<=sz[x];j++) f[x][j]=g[j]%mo;
	}
}

void pre(){
	h[0][0][0]=1;
	for(int k=1;k<=n;k++) {
		memset(s,0,sizeof(s));
		for(int i=0;i<=n;i++) for(int j=0;i+j<=n;j++){
			if (i+j>=k) {
				h[i][j][k]=s[i-j+n]*2+s[i-j+n-1]+s[i-j+n+1];
				if (h[i][j][k]>1e15) h[i][j][k]%=mo;
			}
			if (h[i][j][k-1]) s[i-j+n]+=h[i][j][k-1];
		}
	}
}

int main()
{
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	scanf("%d",&n);
	for(int i=2;i<=n;i++) scanf("%d",&fa[i]),ins(fa[i],i);
	for(int i=1;i<=n;i++) 
	{
		mul[i][0]=1;
		for(int j=1;j<=n;j++) mul[i][j]=mul[i][j-1]*i%mo;
	}
	pre();
	dfs(1,1);
	printf("%lld",f[1][1]);
}