【#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)));
}
posted @ 2019-01-23 21:44  Newuser233  阅读(12)  评论(0)    收藏  举报