BZOJ1912: [Apio2010]patrol 巡逻
又是一道看了题解的题
题意:给出一棵边权为 1 的树,要求一次走完所有的边并回到出发点,规定你可以选择连 k 条边权为 1 的边,并且新连的边必须且只能走一次,求最短路程
显然 k = 1 的时候答案为 (2n - 树的直径 - 1)
现在考虑 k = 2 的时候如何连第二条边
由于每条原来的边至少走一次,连边的策略需要尽可能保证所连接的两点及其 lca 之间的边经过次数 > 1,并选择最长的路径
但显然不是所有情况都有 满足条件的路径 能使得答案最优的
那接下来考虑如何在决策选点对时考虑上之前连的边的影响
以下来自题解
若新连出的环与之前连出的环不重叠,则答案会继续减小
若重叠,两环重叠的部分就不会被巡逻到,又因题目要求至少经过一次,我们不得不在合适的时候经过这条边并返回
最终的后果是两环重叠的部分由之前的“只需要经过一次”变成了“需要经过两次”
再想一下,对于原图,每条边需要经过两次,相当于这条边变回去了
我们这时把第一次求出的直径上的边边权取相反数
再求一遍直径,需要注意的是这一次一定要用 dp 求树的直径,因为有负边权了, bfs 第一次找最深的节点的性质不满足,会搞炸,可以手画样例理解一下
设两次求出的直径分别为 l1, l2
则答案为 (2(n - 1) - (l1 - 1) - (l2 - 1)) = 2n - l1 - l2
以下 copy 自题解
如果 直径l2 包含 直径l1 的部分
当减掉 (l1 - 1) 后,重叠的部分变为“只需要经过一次”,减掉 (l2 - 1) 后,重叠的部分就变回了“需要经过两次”,这正是我们想要的
代码:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cctype> #include<cstdio> #include<queue> using namespace std; const int MAXN = 100005; struct EDGE{ int nxt, to, val; EDGE(int NXT = 0, int TO = 0, int VAL = 0) {nxt = NXT; to = TO; val = VAL;} }edge[MAXN << 1]; int n, k, totedge = 1, mxpt, p, q, l1, l2 = 0xcfcfcfcf; int head[MAXN], dst[MAXN], fa[MAXN], frm[MAXN]; bool vis[MAXN]; inline void add(int x, int y, int v) { edge[++totedge] = EDGE(head[x], y, v); head[x] = totedge; return; } void dfs(int x) { vis[x] = true; for(int i = head[x]; i; i = edge[i].nxt) if(!vis[edge[i].to]) { int y = edge[i].to; dfs(y); l2 = max(l2, dst[x] + dst[y] + edge[i].val); dst[x] = max(dst[x], dst[y] + edge[i].val); } return; } inline void bfs(int bgn) { for(int i = 1; i <= n; ++i) dst[i] = 0x3f3f3f3f, fa[i] = 0, frm[i] = 0; queue<int> que; dst[bgn] = 0; frm[bgn] = 0; que.push(bgn); while(!que.empty()) { int x = que.front(); que.pop(); if(dst[x] > dst[mxpt]) mxpt = x; for(int i = head[x]; i; i = edge[i].nxt) if(dst[edge[i].to] == 0x3f3f3f3f) { int y = edge[i].to; dst[y] = dst[x] + edge[i].val; frm[y] = i; fa[y] = x; que.push(y); } } return; } int main() { scanf("%d%d", &n, &k); register int xx, yy; for(int i = 1; i < n; ++i) { scanf("%d%d", &xx, &yy); add(xx, yy, 1); add(yy, xx, 1); } dst[0] = 0xcfcfcfcf; bfs(1); p = mxpt; mxpt = 0; bfs(p); q = mxpt; while(mxpt != p) { ++l1; edge[frm[mxpt]].val = edge[frm[mxpt] ^ 1].val = -1; mxpt = fa[mxpt]; } if(k == 1) { printf("%d\n", (n << 1) - l1 - 1); return 0; } if(k == 2) { for(int i = 1; i <= n; ++i) dst[i] = 0; dfs(1); if(l2 < 0) l2 = 0; printf("%d\n", (n << 1) - l1 - l2); } return 0; }
禁止诸如开发者知识库/布布扣/码迷/学步园/马开东等 copy 他人博文乃至博客的网站转载
,用户转载请注明出处:https://www.cnblogs.com/xcysblog/