ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

bzoj 2785 jzoj 2755 【2012东莞市选】树的计数(计数+dp):

2020-02-02 15:01:05  阅读:289  来源: 互联网

标签:2785 return a0 int ll jzoj 计数 operator fo


Description:

给出两个整数nd,求出有n个节点并且两个节点间最长距离为d的标号树的个数。

标号树即是树上每个结点都标有一个不同的编号。

\(n<=50\)

题解:

假设直径是偶数,虽然直径可以有很多条,但是直径的中点是确定的。

奇数也差不多,只是中间那条边是定的。

所以设\(g[i][j]\)表示表示\(i\)个点,定根有标号树,深度是\(j\)的方案数。

然而这个东西dp不了:通常的转移是枚举它的一个子树使深度是\(j-1\),但是这样怎么去重都不行。

所以设\(f[i][j]\)表示深度不超过\(j\)的版本的。

这时,一个点的所有子树都是类似的,那么只要保证选的那个子树含有标号最小的点即可。

注意要预先选出根的颜色,但是\(g[n-x][j]\)又给根分配了颜色,所以要除以n-x。

转移:\(g[i][j]=\sum_{x=0}^{i}g[x][j-1]*g[i-x][j]*C_{i-2}^{x-1}*n/(n-x)\)

最后统计答案:

设\(w=d/2\)

直径是偶数:
相当于要有至少两棵=d/2-1的子树,那么用总数-只有一棵的

\(Ans=g[n][w]-\sum C_{n}^x*f[x][w-1]*g[n-x][w-1]\)

直径是奇数的:
一条边的两边都要=d/2,组合数保证最小的点在一边即可。

\(Ans=\sum C_{n-1}^{x-1}*f[x][w]*f[n-x][w]\)

没有取模,需要高精度。

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int N = 55;

int n, d;

const int m = 10;

const ll W = 1e8;

struct P {
    ll a[m + 2];
    P() {
        fo(i, 0, m) a[i] = 0;
    }
};

P operator + (P a, P b) {
    fo(i, 0, m) a.a[i] += b.a[i];
    fo(i, 0, m) {
        a.a[i + 1] += a.a[i] / W;
        a.a[i] %= W;
    }
    return a;
}

P operator - (P a, P b) {
    fo(i, 0, m) a.a[i] -= b.a[i];
    fo(i, 0, m) if(a.a[i] < 0) {
        a.a[i] += W;
        a.a[i + 1] --;
    }
    return a;
}

P operator * (P a, P b) {
    P c = P();
    fo(i, 0, m) fo(j, 0, m - i)
        c.a[i + j] += a.a[i] * b.a[j];
    fo(i, 0, m) {
        c.a[i + 1] += c.a[i] / W;
        c.a[i] %= W;
    }
    return c;
}

P operator * (P a, ll b) {
    fo(i, 0, m) a.a[i] *= b;
    fo(i, 0, m) {
        a.a[i + 1] += a.a[i] / W;
        a.a[i] %= W;
    }
    return a;
}

P operator / (P a, ll b) {
    ll y = 0;
    fd(i, m, 0) {
        a.a[i] += y * W;
        y = a.a[i] % b;
        a.a[i] /= b;
    }
    return a;
}

void print(P a) {
    int a0 = m;
    while(a0 >= 0 && !a.a[a0]) a0 --;
    if(a0 < 0) {
        pp("0");
    } else {
        pp("%lld", a.a[a0]);
        fd(i, a0 - 1, 0) printf("%08lld", a.a[i]);
    }
    hh;
}

P c[N][N], f[N][N], g[N][N];

P ans[N][N];

int main() {
    n = 50;
    fo(i, 0, n) {
        c[i][0].a[0] = 1;
        fo(j, 1, i) c[i][j] = c[i - 1][j] + c[i - 1][j - 1];
    }
    fo(j, 0, n) f[1][j].a[0] = g[0][j].a[0] = 1;
    fo(i, 2, n) {
        fo(j, 1, n) {
            fo(x, 1, i - 1) {
                f[i][j] = f[i][j] + f[x][j - 1] * f[i - x][j] * c[i - 2][x - 1] * i / (i - x);
            }
        }
    }
    fo(i, 0, n) {
        fo(j, 1, n) g[i][j] = f[i][j] - f[i][j - 1];
        g[i][0] = f[i][0];
    }
    for(n = 1; n <= 50; n ++) {
        if(n == 1) ans[n][0].a[0] = 1;
        for(d = 1; d <= n; d ++) {
            int w = d / 2;
            if(d & 1) {
                fo(x, 1, n) ans[n][d] = ans[n][d] + g[x][w] * g[n - x][w] * c[n - 1][x - 1];
            } else {
                ans[n][d] = g[n][w];
                fo(x, 0, n) ans[n][d] = ans[n][d] - g[x][w - 1] * f[n - x][w - 1] * c[n][x];
            }
        }
    }
    while(scanf("%d %d", &n, &d) != EOF)
        print(ans[n][d]);
}

标签:2785,return,a0,int,ll,jzoj,计数,operator,fo
来源: https://www.cnblogs.com/coldchair/p/12252233.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有