[luogu]P3629 [APIO2010]巡逻
Posted _onglu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[luogu]P3629 [APIO2010]巡逻相关的知识,希望对你有一定的参考价值。
原题链接:P3629 [APIO2010]巡逻
题意
给定一棵树,每次要走过每条边。
现在要求加$k$条边,使得走过每条边后走过的距离最小,求这个最小值。
分析
首先不考虑加边,也就是说$k=0$时,是一棵树,显然每条边要经过两次。
考虑加一条边,很容易发现形成的环上的点只需要经过一遍,最终经过的距离就是$((n-1) imes 2-len+2)$,$len$是最大环的长度,明显是树的直径$+1$。
考虑加两条边对答案有什么影响。
分两类考虑:
1.第一次形成的环和第二次没重叠:答案继续减去$len_2+2$.
2.第一次形成的环和第二次有重叠:画过图就很容易发现,重叠部分是经过了两次的,答案就是$((n-1) imes 2-len_1+2_len_2+2+k)$,$k$是这个重叠部分的长度。
那么我们就考虑怎么求出这个重叠部分了。
考虑差分。
第一次已经减去了这个长度,第二次减去这个长度的相反数就相当于加回上了这个长度。
那么我们只需要把直径上的边权置为$-1$,再求一次树的直径即可。
注意第二次的直径可以为$0$(自环)。
实现的方式
我们常用dfs写树的直径,但是我们发现这样子是不能跑负权图的,因为dfs不能统计负权的情况。
而树形dp求树的直径虽然可以跑负权图,但是却很难转移出其他量。
我们结合这两种算法,在第一次时用dfs求出直径,并保存出直径上的边,第二次再跑一次树形dp就可以了。
保存直径上的边的时候,我们需要一点小技巧。
运用费用流中求增广路的方法,我们保存每个节点的祖先节点(从哪里转移过来),并利用成对储存的方法保存每条边和它的反向边。
具体实现见代码。
代码
#include <bits/stdc++.h> using namespace std; const int N=1e5+1000; int read(){ char c;int num,f=1; while(c=getchar(),!isdigit(c))if(c==‘-‘)f=-1;num=c-‘0‘; while(c=getchar(), isdigit(c))num=num*10+c-‘0‘; return f*num; } int n,k,ans,pre[N*2],maxn,id,dis[N*2]; int head[N],nxt[N*2],ver[N*2],edge[N*2],tot=1; void add_edge(int u,int v,int w){ ver[++tot]=v;edge[tot]=w;nxt[tot]=head[u];head[u]=tot; ver[++tot]=u;edge[tot]=w;nxt[tot]=head[v];head[v]=tot; } void dfs(int x,int val,int pr){ if(val>maxn){ maxn=val; id=x; } for(int i=head[x];i;i=nxt[i]){ if(ver[i]==pr)continue; pre[ver[i]]=i; dfs(ver[i],val+edge[i],x); } } int dfs_get_diam(){ dfs(1,0,1); memset(pre,0,sizeof(pre)); maxn=0; dfs(id,0,id); return maxn; } void dp_get_diam(int x,int fa){ for(int i=head[x];i;i=nxt[i]){ if(ver[i]==fa)continue; dp_get_diam(ver[i],x); maxn=max(maxn,dis[x]+dis[ver[i]]+edge[i]); dis[x]=max(dis[x],dis[ver[i]]+edge[i]); } } int main() { n=read();k=read(); for(int i=1;i<n;i++){ int u,v; u=read();v=read(); add_edge(u,v,1); ans+=2; } ans-=dfs_get_diam()-1; if(k==1){ printf("%d ",ans); return 0; } for(int i=pre[id];i;i=pre[ver[i^1]]){ edge[i]=-1; edge[i^1]=-1; } maxn=0; dp_get_diam(1,1); ans-=maxn-1; printf("%d ",ans); return 0; }
以上是关于[luogu]P3629 [APIO2010]巡逻的主要内容,如果未能解决你的问题,请参考以下文章