洛谷P3629 [APIO2010]巡逻

题意

传送门

题解

$k=0$ 时显然每条边经过一次,答案为 $2*(n-1)$
$k=1$ 时可以发现,新路连的两个端点 $i,j$ 之间形成环,环上所有边都不需要经过两次,相当于 $k=0$ 时的答案减去 $i,j$ 间距再 $+1$ (新路必须经过),显然 $i,j$ 距离最大时最优,故取树上最长链。
$k=2$时可以发现,在 $k=1$ 基础上再连一条边不能直接取次长链,因为每经过 $k=1$ 选取的链上的边,你将会多走 $2$ 步,故将原来最长链上的边变成 $-1$ ,再求最长链,再将 $k=1$ 答案减去 $+1$ 即可。

调试记录

  • 第二次求最长链的时候求错了(dis的最小值太大了)

代码

#include <bits/stdc++.h>
using namespace std;

struct edge {
    int to, nxt;
} edges[200005];
int e = 1, hed[100005], dis[100005], fa[100005], rt=0;
map<pair<int, int>, bool> M;

void addedge(int x, int y) {
    edges[e] = (edge){y, hed[x]};
    hed[x] = e++;
}
void dfs(int x, int fa) {
    for (int e=hed[x]; e; e=edges[e].nxt) {
        int y = edges[e].to;
        if (y != fa) {
            dis[y] = max(dis[x] + (M[make_pair(x, y)] ? -1 : 1), 0);
            ::fa[y] = x;
            dfs(y, x);
        }
    }
}
int lca(int x,int y) {
    if (dis[x] > dis[y]) swap(x, y);
    while (dis[x] != dis[y]) y = fa[y];
    while (x != y) x = fa[x], y = fa[y];
    return x;
}
void up(int x, int dest) {
    if (x == rt || x == dest) return;
    M[make_pair(x, fa[x])] = true;
    M[make_pair(fa[x], x)] = true;
    up(fa[x], dest);
}

int main() {
    int n, k;
    scanf("%d%d", &n,&k);
    for (int i=1; i<n; i++) {
        int a, b; scanf("%d%d", &a, &b);
        addedge(a, b); addedge(b, a);
    }
    if (k == 0) {
        printf("%d\n", 2*(n-1));
        return 0;
    }
    dfs(1, 0);
    int mx=0, mxid=1;
    for (int i=1; i<=n; i++) if (dis[i]>mx) mx=dis[i], mxid=i;
    memset(dis, 0, sizeof(dis));
    dfs(mxid, 0); fa[mxid] = mxid;
    int nmxid=mxid; mx=0;
    for (int i=1; i<=n; i++) if (dis[i]>mx) mx=dis[i], nmxid=i;
    if (k == 1) {
        printf("%d\n", 2*(n-1)-dis[nmxid]+1);
        return 0;
    }
    ::rt = mxid;
    int l = lca(mxid, nmxid);
    up(mxid, l); up(nmxid, l);
    int xa = 2*(n-1)-dis[nmxid]+1;
    memset(dis, 0, sizeof(dis));
    dfs(1, 0);
    mx=0, mxid=1;
    for (int i=1; i<=n; i++) if (dis[i]>mx) mx=dis[i], mxid=i;
    memset(dis, 0, sizeof(dis));
    dfs(mxid, 0);
    nmxid=mxid; mx=0;
    for (int i=1; i<=n; i++) if (dis[i]>mx) mx=dis[i], nmxid=i;
    int ans=min(xa, xa-dis[nmxid]+1);
    if (ans/100000) if (ans%10==6) ans=ans/10*10+2;
    printf("%d\n", ans);
    return 0;
}
posted @ 2018-08-08 19:52  MCH__ds  阅读(174)  评论(0编辑  收藏  举报