【#6495. 「雅礼集训 2018 Day1」树】期望\计数dp
自从pion之后就主要专注在数学和数据结构。但事实上dp是一个神奇的东西,已经记得不知道多少次被卡死在这个东西上了orz orz orz ,尤其连跪两天期望dp,orz orz orz.今天考了三道dp,最后代码都短得吓人,但其中自闭之感orz orz orz
https://loj.ac/problem/6495
感谢某位大神的题解
题目描述
有一棵 n 个点的有根树,点编号为 1 至 n,其中 1 号点为根,除 1 号点外,i 号点的父亲在 1 至 i - 1 内均匀随机。
定义一棵树的深度为所有节点到根路径上节点数的最大值,求这棵树的期望深度。
输入格式
输入包含一行两个正整数 n, p 的意义见输出格式。
输出格式
输出包含两行,每行一个非负整数,第一行表示答案四舍五入成整数的值,第二行表示答案在模 pp 意义下的值。
样例
样例输入 1
3 233
样例输出 1
3
119
数据范围与提示
对于全部数据 1≤n≤24,100≤p≤10^9+7,p为质数 。
注意:在本题的子任务 1 中包含三个 p 不为质数的测试点。为了得到与标准输出一样的结果,请使用费马小定理求逆元而不是线性递推。
n=24很容易想到状压,然后,然后,尽情自闭。
好吧,考虑更为优秀的做法,我们考虑算方案数然后计算贡献,最后除以总方案数$ (n-1)! $.
状态:$F(i,j) $表示第i个点深度为j的方案数。
转移:我们发现二号点的父亲必须为1,那么我们转移的时候可以通过枚举2号所在的那棵子树的大小,并且当剩下的以1当根的那一部分(不包括2子树)高度小于j时,那么2号的子树必须深度为j-1,否则就是1当根(不包括2子树)深度为j时,2号树根随便选,然后乘上组合数就转移了。
$$F(i,j) = \sum\limits_{x=1}^{i-1} [ \sum\limits_{y=1}^{j-2}F(i-x) * F(x,y) * \binom{i-2}{x-1} + \sum\limits_{y=1}^{j} F(i-x,y) * F(x,j-1) * \binom{i-2}{x-1} ] $$
$O(n^4)$
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef double db;
int n,mod;
int add(int x,int y) { x+=y; return x>=mod?x-mod:x; }
int sub(int x,int y) { x-=y; return x<0?x+mod:x; }
int mul(int x,int y) { return 1ll*x*y%mod; }
int ksm(int a,int b) {
int ans = 1;
for(;b;b>>=1,a=mul(a,a))
if(b&1) ans = mul(ans,a);
return ans;
}
int f[50][50];
int fac[55],inv[55];
int C[55][55];
db c[50][50];
db g[50][50];
int GC(int x,int y) { return C[x][y]; }
db gc(int x,int y) { return c[x][y]; }
int main() {
scanf("%d%d",&n,&mod);
C[0][0] = 1;c[0][0] = 1;
for(int i=1;i<=n;i++) {
C[i][0] = 1; c[i][0] = 1;
for(int j=1;j<=i;j++) {
C[i][j] = add(C[i-1][j],C[i-1][j-1]);
c[i][j] = c[i-1][j] + c[i-1][j-1];
}
}
f[1][1] = 1; g[1][1] = 1;
int ff = 1; inv[0] = 1; fac[0] = 1;
for(int i=1;i<n;i++) ff = mul(ff,i);
for(int i=2;i<=n;i++) {
for(int j=2;j<=i;j++) {
for(int x=1;x<=i-1;x++) {
int mm = 0; db mo = 0;
for(int y=1;y<=j-2;y++) mm = add(mm,mul(GC(i-2,x-1),mul(f[i-x][j],f[x][y]))),mo=mo+gc(i-2,x-1)*g[i-x][j]*g[x][y];
for(int y=1;y<=j;y++) mm = add(mm,mul(GC(i-2,x-1),mul(f[i-x][y],f[x][j-1]))),mo=mo+gc(i-2,x-1)*g[i-x][y]*g[x][j-1];
f[i][j] = add(f[i][j],mm);
g[i][j] += mo;
}
}
}
int sm = 0; db fm = 0;
for(int i=1;i<=n;i++) {
sm = add(sm,mul(i,f[n][i]));
fm += g[n][i]*i;
}
for(int i=1;i<n;i++) fm/=i;
printf("%d \n%d",(int)(fm+0.5),mul(sm,ksm(ff,mod-2)));
}