树的直径初探+Luogu P3629 [APIO2010]巡逻【树的直径】By cellur925
我们先来介绍一个概念:树的直径。
树的直径:树中最远的两个节点间的距离。(树的最长链)
树的直径有两种方法,都是$O(N)$。
第一种:两遍bfs/dfs(这里写的是两遍bfs)
从任意一个节点出发,遍历一遍树找到与出发点距离最远的点p。
再从节点p出发,遍历一遍求出与p距离最远的点q。则pq即为直径(其中一个)
但是不能处理负权边。
int bfs(int x) { queue<int>q; memset(d,0x3f,sizeof(d)); memset(pre,0,sizeof(pre)); fake=d[233]; q.push(x);d[x]=0; while(!q.empty()) { int u=q.front();q.pop(); for(int i=head[u];i;i=edge[i].next) { int v=edge[i].to; if(d[v]==fake) q.push(v),pre[v]=i,d[v]=d[u]+1; } } int top=x; for(int i=1;i<=n;i++) if(d[i]>d[top]) top=i; return top; } int get_d() { p=bfs(1); p=bfs(p); return d[p]; }
第二种:树形dp
void Treedp(int u) { vis[u]=1; for(int i=head[u];i;i=edge[i].next) { int v=edge[i].to; if(vis[v]) continue; Treedp(v); ans=max(ans,f[x]+f[y]+edge[i].val) f[x]=max(f[x],f[y]+edge[i].val); } }
题目大意:给你一棵树,你需要把这棵树上的每条边至少遍历一次,走过一条边的代价是1,现在你可以添加1或2条边,新添的边可且仅可遍历一次,问最小代价是多少。
不加边的时候,答案就是$2*(n-1)$。
加一条新道路后,因为新道路必须经过恰好一次,设$l$,$r$为新建道路的两端,那么从$l$去$r$的时候走新道路,回来的时候走原来的道路。形成了一个环,也就是说其他与$l$,$r$无关的点还是走过两次,而连接$l$,$r$的路径上的边走一次就行了。而我们贪心的选择树中最长的路径,那就是树的直径。设直径为$d$,那么答案就是$2*(n-1)-d+1$。(+1是新建的那条道路)
加两条新道路后,也会形成一个环,但是我们不知道这个环与之前的那个环是不是有重叠关系。若重叠,那么两个环重叠的部分就不会被经过;不重叠还好说。那么我们如何解决?处理重叠情况,我们的目标是使重叠部分恰好经过两次,那么我们可以把第一次的直径上的所有边取反(1变成-1),再在取反了的树上找直径。最终答案就是
$2*(n-1)-(l1-1)-(l2-1)$。
Code
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<queue> 5 #define maxn 100090 6 7 using namespace std; 8 9 int n,k,tot=1,fake,p,ans1,ans2; 10 int head[maxn],d[maxn],pre[maxn],vis[maxn],f[maxn]; 11 struct node{ 12 int to,next,val; 13 }edge[maxn*2]; 14 15 void add(int x,int y) 16 { 17 edge[++tot].to=y; 18 edge[tot].next=head[x]; 19 head[x]=tot; 20 edge[tot].val=1; 21 } 22 23 int bfs(int s) 24 { 25 queue<int>q; 26 memset(pre,0,sizeof(pre)); 27 memset(d,0x3f,sizeof(d)); 28 fake=d[233]; 29 q.push(s);d[s]=0; 30 while(!q.empty()) 31 { 32 int u=q.front();q.pop(); 33 for(int i=head[u];i;i=edge[i].next) 34 { 35 int v=edge[i].to; 36 if(d[v]==fake) d[v]=d[u]+edge[i].val,pre[v]=i,q.push(v); 37 } 38 } 39 int top=s; 40 for(int i=1;i<=n;i++) if(d[i]>d[top]) top=i; 41 return top; 42 } 43 44 void get_d() 45 { 46 p=bfs(1); 47 p=bfs(p); 48 } 49 50 void Treedp(int u) 51 { 52 vis[u]=1; 53 for(int i=head[u];i;i=edge[i].next) 54 { 55 int v=edge[i].to; 56 if(vis[v]) continue; 57 Treedp(v); 58 ans2=max(ans2,f[u]+f[v]+edge[i].val); 59 f[u]=max(f[u],f[v]+edge[i].val); 60 } 61 } 62 63 int main() 64 { 65 scanf("%d%d",&n,&k); 66 for(int i=1;i<=n-1;i++) 67 { 68 int x=0,y=0; 69 scanf("%d%d",&x,&y); 70 add(x,y),add(y,x); 71 } 72 get_d(); 73 ans1=d[p]; 74 if(k==1){printf("%d",2*(n-1)-d[p]+1);return 0;} 75 for(int i=pre[p];i;i=pre[edge[i^1].to]) 76 edge[i].val=-1,edge[i^1].val=-1; 77 Treedp(1); 78 printf("%d",2*(n-1)-(ans1-1)-(ans2-1)); 79 return 0; 80 }
独立意志与自由思想是必须争的,且须以生死力争。