HDU 4607 Park Visit
题意:一个有N个结点的无根树,我们要访问K个点,问最小路径是多少。
解:我们设树的直径是dia,直径上经过的结点个数为num
-
当K <= num时,ans = K-1. (从直径的一端沿着直径走)
-
others,ans = dia + (K - num) * 2(直径一定是走完的,还要走直径的分支,并且分支是走重复的,也就是两倍)
举个栗子!!!
如果K == 8
如果K == 9
最开始的时候也是不明白K > num的时候到底是什么鬼的公式,但是事实是画个图就明白了。哈哈!!
求树的直径
一、两遍dfs的做法
-
以任一个结点为根,将无根树转为有根树,找到它的最大距离,并记到达的结点为nod1。
-
以nod1为根,跑dfs,找到它的最大距离,到达的结点为nod2。显而易见nod1和nod2两个结点间的距离就是dia.
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxN = 1e5 + 5;
int N, Q, k;
int head[maxN], cnt;
struct EDGE{
int adj, to;
EDGE(int a = -1, int b = 0): adj(a), to(b) {}
}edge[maxN << 1];
void add_edge(int u, int v)
{
edge[cnt] = EDGE(head[u], v);
head[u] = cnt ++;
}
struct DIA{
int nod1, nod2, dia;
DIA(int a = 0, int b = 0, int c = 0): nod1(a), nod2(b), dia(c) {}
}info;
void init()
{
memset(head, -1, sizeof(head));
cnt = 0;
info = DIA(0, 0, 0);
}
int dis[maxN];
void dfs(int x, int fa, int d, int flag)
{
dis[x] = d;
if(info.dia < dis[x])
{
info.dia = dis[x];
if(flag == 0)
info.nod1 = x;
else if(flag == 1)
info.nod2 = x;
}
for(int i = head[x]; ~i; i = edge[i].adj)
{
if(edge[i].to == fa)
continue;
dfs(edge[i].to, x, d + 1, flag);
}
}
int main()
{
int T; scanf("%d", &T);
while(T -- )
{
scanf("%d%d", &N, &Q);
init();
for(int i = 0; i < N - 1; i ++ )
{
int u, v;
scanf("%d%d", &u, &v);
add_edge(u, v);
add_edge(v, u);
}
dfs(1, 0, 0, 0);
dfs(info.nod1, 0, 0, 1);
while(Q -- )
{
scanf("%d", &k);
if(k <= info.dia)
printf("%d\n", k - 1);
else
printf("%d\n", info.dia + (k - (info.dia + 1)) * 2);
}
}
return 0;
}
二、树形DP求解树的直径
树形DP详解
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxN = 1e5 + 5;
int N, Q, k;
int head[maxN] ,cnt;
struct EDGE{
int adj, to;
EDGE(int a = 0, int b = 0): adj(a), to(b) {}
}edge[maxN << 1];
void add_edge(int u, int v)
{
edge[cnt] = EDGE(head[u], v);
head[u] = cnt ++ ;
}
int dia;
int first[maxN], second[maxN], upp[maxN];
int son_vis[maxN];
void dfs_down(int x, int fa)//向下遍历
{
first[x] = 0;
second[x]= 0;
for(int i = head[x]; ~i; i = edge[i].adj)
{
if(edge[i].to == fa)
continue;
dfs_down(edge[i].to, x);
if(first[x] < first[edge[i].to] + 1)
{
second[x] = first[x];
first[x] = first[edge[i].to] + 1;
son_vis[x] = edge[i].to;
}
else if(second[x] < first[edge[i].to] + 1)
second[x] = first[edge[i].to] + 1;
}
}
void dfs_up(int x, int fa)//向上遍历
{
for(int i = head[x]; ~i; i = edge[i].adj)
{
if(edge[i].to == fa)
continue;
if(son_vis[x] == edge[i].to)
upp[edge[i].to] = max(second[x], upp[x]) + 1;
else
upp[edge[i].to] = max(first[x], upp[x]) + 1;
dfs_up(edge[i].to, x);
}
dia = max(dia, upp[x] + first[x]);
}
void init()
{
memset(head, -1, sizeof(head));
cnt = 0;
dia = 0;
upp[1] = 0;
memset(son_vis, -1, sizeof(son_vis));
}
int main()
{
int T; scanf("%d", &T);
while(T -- )
{
scanf("%d%d", &N, &Q);
init();
for(int i = 0; i < N -1; i ++ )
{
int u, v;
scanf("%d%d", &u, &v);
add_edge(u, v);
add_edge(v, u);
}
dfs_down(1, 0);
dfs_up(1, 0);
for(int i = 0; i < Q; i ++ )
{
scanf("%d", &k);
if(k <= dia + 1)
printf("%d\n", k - 1);
else
printf("%d\n", dia + (k - (dia + 1)) * 2);
}
}
return 0;
}