P3629 [APIO2010]巡逻(树的直径)

题目链接


易错点:

  • l2必须要设置全局变量而不能设置局部变量,这是由于设置局部变量无法兼顾所有情况造成的.
  • bfs并设置直径上边权为-1后,如果k=2则不能继续直接使用bfs获得答案,这是bfs的拓展加点性造成的(类比dijkstra).
  • 如果两个变量可以用一个变量导出就用一个变量. 

BFS方法正确性的证明:

  • 如果源点在直径上:显然正确.
  • 如果源点不在直径上:既然源点不在直径上那么直径一定在源点的某棵子树内。由树和直径的性质可知此时直接取最远点然后再进行一次bfs即可求得直径.

#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int MAXN=2e5,INF=0x3f3f3f3f;
struct Edge{
	int from,to,w,nxt;
}e[MAXN*2];
int head[MAXN],edgeCnt=1;
void addEdge(int u,int v,int w){
	e[++edgeCnt].from=u;
	e[edgeCnt].to=v;
	e[edgeCnt].w=w;
	e[edgeCnt].nxt=head[u];
	head[u]=edgeCnt;
}
int n;
int d[MAXN],pre[MAXN];
int bfs(int p){
	memset(d,0x3f,sizeof(d));
	queue<int> q;
	q.push(p);
	d[p]=pre[p]=0;
	while(!q.empty()){
		int nowNode=q.front();q.pop();
		for(int i=head[nowNode];i;i=e[i].nxt){
			int nowV=e[i].to;
			if(d[nowV]==INF){
				d[nowV]=d[nowNode]+e[i].w;
				pre[nowV]=i;
				q.push(nowV);
			}
		}
	}
	int ans=1;
	for(int i=1;i<=n;i++){
		if(d[i]>d[ans])ans=i;//如果两个变量可以用一个变量导出就用一个变量. 
	}
	return ans;
}
int p;
int get(){
	p=bfs(1);
	p=bfs(p);
	return d[p];
}
void change(){
	for(;pre[p];p=e[pre[p]].from){
		e[pre[p]].w=e[pre[p]^1].w=-1;
	}
}
bool vis[MAXN];
int f[MAXN],l2;
void dfs(int p){
	vis[p]=1;
	for(int i=head[p];i;i=e[i].nxt){
		int nowV=e[i].to;
		if(!vis[nowV]){
			dfs(nowV);
			l2=max(l2,f[p]+f[nowV]+e[i].w);
			f[p]=max(f[p],f[nowV]+e[i].w);
		}
	}
}
int main(){
	int k;
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n-1;i++){
		int a,b;
		scanf("%d%d",&a,&b);
		addEdge(a,b,1);
		addEdge(b,a,1);
	}
	int l1=get();
	if(k==1){
		printf("%d
",2*(n-1)-l1+1);
		return 0;
	}else{
		change();
		dfs(1);
		printf("%d
",2*n-l1-l2);
		return 0;
	}
}
原文地址:https://www.cnblogs.com/zbsy-wwx/p/11680565.html