码迷,mamicode.com
首页 > 其他好文 > 详细

GMOJ 6815. 【2020.10.06提高组模拟】树的重心

时间:2020-10-10 16:38:59      阅读:31      评论:0      收藏:0      [点我收藏+]

标签:==   break   amp   efi   --   大小   ceil   维表   提高   

6815.【2020.10.06提高组模拟】树的重心


 

Description


给定一棵大小为 n 的树, 求所有大小为 k 的连通块的编号较小的重心的权值和.

 

Solution


首先, 一个点是重心的充要条件是 :

1.\(siz_u \geq \lceil \frac{k}{2} \rceil\).
2.对于 u 的任意儿子 v, \(siz_v \leq \lfloor \frac{k}{2} \rfloor\).

那么便可以再暴力DP上做出一点改动(暴力 设 \(f_{u, i}\) 表示在以 u 为根的子树中,选出包含 u 的大小为 i 的连通块的方案数),
扩展为 \(f{u, i, 0/1}\), 最后一维表示 u 的是否存在子树的大小超过 \(\lfloor \frac{k}{2} \rfloor\).
转移应该十分显然.

再考虑怎么求答案, 设 \(g_{u, i}\) 表示以 u 为根的子树中, 选出包含 u 的大小为 i 的连通块的所有可能的重心权值和.
这时候之前对于暴力状态的扩展就体现出用处来了.
首先考虑 k 为奇数时 g 的转移 :

\[ g_{u, i} = \begin{cases} val_{u} \times f_{u, i - j, 0} \times f_{v, j, 0} & ({i - j \leq \lceil \frac{k}{2} \rceil, j \leq \lfloor \frac{k}{2} \rfloor}) \\[2ex] g_{u, i - j} \times f_{v, j, 0} & ({i - j > \lceil \frac{k}{2} \rceil}) \\[2ex] g_{v, j} \times f_{u, i - j, 0} & ({j > \lfloor \frac{k}{2} \rfloor + 1}) \\[2ex] \end{cases} \]

至于 k 为偶数, 第二条转移时需要特判 \(v < u, siz_v = \frac{k}{2}\) 的情况, 此时应用 \(val_v\) 转移.

最后答案就是 \(\sum{g_{u, k}}\)

 

Code

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

#define N 50000
#define K 500
#define Mod 1000000007

#define fo(i, x, y) for(int i = x, end_i = y; i <= end_i; i ++)
#define fd(i, x, y) for(int i = x, end_i = y; i >= end_i; i --)
#define Fo(i, u) for(int i = head[u]; i; i = edge[i].next)

void read(int &x) {
    char ch = getchar(); x = 0;
    while (ch < ‘0‘ || ch > ‘9‘) ch = getchar();
    while (ch >= ‘0‘ && ch <= ‘9‘) x = (x << 1) + (x << 3) + ch - 48, ch = getchar();
}

struct EDGE { int next, to; } edge[N << 1];

int head[N + 1], siz[N + 1], f[N + 1][K + 1][2], g[N + 1][K + 1], h[K + 1], val[N + 1], q[N + 1];

int n, m;

int cnt_edge = 1;
void Add(int u, int v) { edge[ ++ cnt_edge ] = (EDGE) { head[u], v }, head[u] = cnt_edge; }
void Link(int u, int v) { Add(u, v), Add(v, u); }

void Input() {
    int x, y;
    read(n), read(m);
    fo(i, 1, n) read(val[i]);
    fo(i, 2, n)
        read(x), read(y), Link(x, y);
}

int Min(int x, int y) { return x < y ? x : y; }
int Max(int x, int y) { return x > y ? x : y; }

void Bfs() {
    q[1] = 1; int u = 0, v = 0, flag = 0;
    for (int t = 1; t; ) {
        u = q[t], flag = 0;
        if (! siz[u]) f[u][1][0] = 1, siz[u] = 1;
        Fo(l, u) if (edge[l].to != q[t - 1]) {
            head[u] = edge[l].next;
            v = edge[l].to;
            q[ ++ t ] = v;
            flag = 1;
            break;
        }
        if (flag) continue;
        -- t;
        if (t) {
            v = q[t + 1], u = q[t];
            fo(i, 1, m) h[i] = g[u][i];
            fd(i, Min(m, siz[u] + siz[v]), 1) {
                fo(j, Max(1, i - siz[u]), Min((m >> 1), Min(siz[v], i))) {
                    if (i - j > (m >> 1) + (m & 1))
                        (h[i] += 1ll * g[u][i - j] * f[v][j][0] % Mod) %= Mod;
                    else
                        (h[i] += 1ll * ((! (m & 1) && v < u && (j << 1) == m) ? val[v] : val[u]) * f[v][j][0] % Mod * f[u][i - j][0] % Mod) %= Mod;
                    (f[u][i][0] += 1ll * f[u][i - j][0] * f[v][j][0] % Mod) %= Mod;
                    if (i - j > (m >> 1) + 1)
                        (f[u][i][1] += 1ll * f[u][i - j][1] * f[v][j][0] % Mod) %= Mod;
                }

                fo(j, Max((m >> 1) + 1, i - siz[u]), Min(siz[v], i)) {
                    (h[i] += 1ll * g[v][j] * f[u][i - j][0] % Mod) %= Mod;
                    (f[u][i][1] += 1ll * f[u][i - j][0] * (f[v][j][0] + f[v][j][1]) % Mod) %= Mod;
                }
                g[u][i] = h[i];
            }
            siz[u] += siz[v];
        }
    }
}

int main() {
    freopen("centroid.in", "r", stdin);
    freopen("centroid.out", "w", stdout);

    Input();
    Bfs();

    int ans = 0;
    fo(i, 1, n) (ans += g[i][m]) %= Mod;
    printf("%d\n", ans);

    return 0;
}

GMOJ 6815. 【2020.10.06提高组模拟】树的重心

标签:==   break   amp   efi   --   大小   ceil   维表   提高   

原文地址:https://www.cnblogs.com/zhouzj2004/p/13787756.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!