其他分享
首页 > 其他分享> > P3629 [APIO2010] DFS + 树形 DP

P3629 [APIO2010] DFS + 树形 DP

作者:互联网

题意

传送门 P3629 [APIO2010]巡逻

题解

当 K = 0 K=0 K=0,遍历整棵树,每条边必然被递归依次,回溯一次,故路线总长度为 2 × ( n − 1 ) 2\times (n-1) 2×(n−1)。

当 K = 1 K=1 K=1,由于新增的边必须正好经过一次,那么由于新增边而构成的环需要遍历一次,则环上的树边经过次数都减少一次,那么目标为最大化环上的树边,那么树的直径即可减小的最大权值。

当 K = 2 K=2 K=2,新增的边又构成一个环,若两条新增道路构成的环不重叠,那对于新增的环上的树边贡献依然是 − 1 -1 −1,若存在重叠,由于环上的边需遍历一次,故贡献为 1 1 1。目标是使总距离最小,那么对于 K = 1 K=1 K=1 情况的环上树边的边权取负,再次计算树的直径,即此时可减小的最大权值。

需要证明迭代求树的直径可以求解 K = 2 K=2 K=2 情况下的答案。反证法,问题可以转化为求树上无相同边的 2 2 2 条路径的最大边权和,假设 2 2 2 条路径完全不经过树的直径,那么用树的直径替换其中一条路径可以获得更大的边权和;假设 2 2 2 条路径部分与树的直径重叠,根据树的直径的最长性,可以替换得到不小于当前边权和的 2 2 2 条新路径。故树的直径一定处于新增边所在的环上。

处理环上边权取负时,使用 D F S DFS DFS 求解树的直径,记录直径上的边;但 D F S DFS DFS 无法处理边权为负的情况,于是使用树形 D P DP DP 求解 K = 2 K=2 K=2 情况下树的直径。

#include <bits/stdc++.h>
using namespace std;
#define maxn 100005
int N, K, d[maxn], pree[maxn];
int head[maxn << 1], nxt[maxn << 1], to[maxn << 1], cost[maxn << 1], tot = 1;

void add(int x, int y, int z)
{
    to[++tot] = y, cost[tot] = z, nxt[tot] = head[x], head[x] = tot;
}

void dfs(int x, int f, int &t)
{
    if (d[x] > d[t])
        t = x;
    for (int i = head[x]; i; i = nxt[i])
    {
        int y = to[i], z = cost[i];
        if (y != f)
            d[y] = d[x] + z, pree[y] = i, dfs(y, x, t);
    }
}

void dp(int x, int f, int &t)
{
    for (int i = head[x]; i; i = nxt[i])
    {
        int y = to[i], z = cost[i];
        if (y != f)
        {
            dp(y, x, t);
            t = max(t, d[x] + d[y] + z);
            d[x] = max(d[x], d[y] + z);
        }
    }
}

int main()
{
    scanf("%d%d", &N, &K);
    for (int i = 1; i < N; ++i)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b, 1);
        add(b, a, 1);
    }
    int s = 1, t = 0, res = (N - 1) << 1;
    dfs(s, 0, t);
    s = t, t = 0, d[s] = pree[s] = 0;
    dfs(s, 0, t);
    res -= d[t] - 1;
    if (K == 2)
    {
        for (int i = pree[t]; i; i = pree[to[i ^ 1]])
            cost[i] = cost[i ^ 1] = -1;
        memset(d, 0, sizeof(d));
        t = 0;
        dp(1, 0, t);
        res -= t - 1;
    }
    printf("%d\n", res);
    return 0;
}

标签:head,环上,int,APIO2010,DFS,P3629,cost,直径,DP
来源: https://blog.csdn.net/neweryyy/article/details/111186872