【学习笔记】树的直径
定义
树的直径定义为树上任意两点间最长的简单路径
求法1:两次dfs
适用范围:树上所有边边权都非负
算法过程:
以树上任意一点开始第一次dfs,找到距其最远的点\(z\),再以\(z\)为起始点进行第二次dfs,找到距其最远的点\(z\prime\),则\(zz\prime\)即为所求。
代码:
void Dfs(int x,int fa,int dis) {
if(dis > mmax) {
mmax = dis;
flag = x;
}
for(auto i:E[x]) {
if(i.first == fa) continue;
Dfs(i.first,x,dis + i.second);
}
}
void SolveDiameterOfTree() {
mmax = 0;
flag = 0;
Dfs(1,0,0);
int z = flag;
//cout << z << "\n";
mmax = 0;
flag = 0;
Dfs(z,0,0);
int zp = flag;
z1 = z;
zp1 = zp;
}
例题
P6722「MCOI-01」Village 村庄
首先注意到有二分图必有奇环。
同时由于这张图的特殊构造,有奇环必有三元环。
同时,该三元环必包含直径的两个端点。
那么枚举剩下的一个点,判断该三元环是否符合要求即可。
代码:
#include<iostream>
#include<vector>
using namespace std;
int dis1[100010],dis2[100010],t,n,k,x,y,v,mmax,flag,z1,zp1;
vector<pair<int,int> > E[100010];
void Dfs(int x,int fa,int dis) {
if(dis > mmax) {
mmax = dis;
flag = x;
}
for(auto i:E[x]) {
if(i.first == fa) continue;
Dfs(i.first,x,dis + i.second);
}
}
void Dfs2(int x,int fa,int dis) {
dis1[x] = dis;
for(auto i:E[x]) {
if(i.first == fa) continue;
Dfs2(i.first,x,dis + i.second);
}
}
void Dfs3(int x,int fa,int dis) {
dis2[x] = dis;
for(auto i:E[x]) {
if(i.first == fa) continue;
Dfs3(i.first,x,dis + i.second);
}
}
void SolveDiameterOfTree() {
mmax = 0;
flag = 0;
Dfs(1,0,0);
int z = flag;
//cout << z << "\n";
mmax = 0;
flag = 0;
Dfs(z,0,0);
int zp = flag;
z1 = z;
zp1 = zp;
}
void CalcDis1(int z1) {
Dfs2(z1,0,0);
}
void CalcDis2(int zp1) {
Dfs3(zp1,0,0);
}
int main()
{
cin >> t;
loop:while(t--) {
cin >> n >> k;
for(int i = 1;i <= n;i++) {
E[i].clear();
}
for(int i = 1;i <= n - 1;i++) {
cin >> x >> y >> v;
E[x].push_back({y,v});
E[y].push_back({x,v});
}
SolveDiameterOfTree();
//cout << z1 << " " << zp1 << "\n";
CalcDis1(z1);
CalcDis2(zp1);
for(int i = 1;i <= n;i++) {
//cout << dis1[i] << " " << dis2[i] << "\n";
if(dis1[i] >= k && dis2[i] >= k) {
cout << "Baka Chino" << "\n";
goto loop;
}
}
cout << "Yes" << "\n";
}
}