P3576 题解

题目链接

我们来看一下这道题:

树上问题,容易想到树形 $dp$

设 $dp_{i}$ 表示指定边到第 $i$ 条边要分叉多少?

容易得到

$$dp_v = dp_u \times (num_u - 1)$$

其中 $num_i$ 为节点 $i$ 的度。

但这样在算结果时会超时。

有没有更好的方法呢?没有

显然,会被吃一定是有范围的,

那么不妨设 $dp_{i,0/1}$ 表示从 $i$ 节点走到指定边会被吃的上界 $/$ 下界,

即从 $i$ 出发 $p$ 个蚂蚁,

若 $dp_{i, 0} \leq p \leq dp_{i, 1}$,则到指定边时会有 $k$ 只蚂蚁,

若不在这个范围内,则不会被吃。

容易得到:

$$dp_{v, 0} = dp_{u, 0} \times (num_u - 1)$$

$$dp_{v, 1} = (dp_{v, 0} + 1) \times (num_u - 1) - 1 $$

既然已经得到了每个点的上下界,那出入口的上下界也得到了,废话

最终就可以二分查找得出结果

复杂度 $O (n log (g))$

我们可以把指定边 $(x, y)$ 拆分成 $(0, x)$ 与 $(0, y)$,$0$ 号节点当作树的根节点,且上文的指定边到 $i$ 号节点就是 $0$ 号节点到 $i$ 号节点。

具体请见代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define int long long // 要开 long long

const int maxn = 1e6 + 10;
struct node {
    int to, nxt;
} edge[maxn >> 1];
int cnt, head[maxn]; // 链式前向星
int n, m, k, ans = 0, a[maxn], num[maxn], dp[maxn][2];

void add (int u, int v) {
    cnt ++;
    edge[cnt].to = v;
    edge[cnt].nxt = head[u];
    head[u] = cnt;
} // 加边

void add_edge (int u, int v) {
    add (u, v); add (v, u);
    num[u] ++; num[v] ++;
} // 别忘了更新 num

void dfs (int u, int fa) {
    dp[u][0] = dp[fa][0] * (num[fa] - 1);
    dp[u][1] = (dp[fa][1] + 1) * (num[fa] - 1) - 1;
    for (int i = head[u]; i; i = edge[i].nxt) {
        int v = edge[i].to;
        if (v == fa) continue;
        dfs (v, u);
    }
} // 树形 dp

signed main() {
    scanf ("%lld%lld%lld", &n, &m, &k);
    for (int i = 1; i <= m; ++i) {
        scanf ("%lld", &a[i]);
    }
    sort (a + 1, a + m + 1); // 不要忘记排序哦!

    int x, y; scanf ("%lld%lld", &x, &y); x ++; y ++;
    add_edge (1, x); add_edge (1, y);
    for (int i = 2; i < n; ++i) {
        int u, v; scanf ("%lld%lld", &u, &v); u ++; v ++;
        add_edge (u, v);
    }
    dp[1][0] = dp[1][1] = k;
    dfs (x, 1); dfs (y, 1);

    for (int i = 2; i <= n + 1; ++i) {
        if (num[i] == 1) {
            ans += upper_bound (a + 1, a + m + 1, dp[i][1]) - lower_bound (a + 1, a + m + 1, dp[i][0]); // 上界查找与下界查找
        }
    }
    printf ("%lld", ans * k); // 乘 k 别忘了
    return 0;
}
posted @ 2021-09-25 11:40  wangzhongyuan  阅读(3)  评论(0编辑  收藏  举报  来源