题目
poj3107
题意:
有一棵树,以某个结点为根并切断这个结点使得它下一层子树的最大连通度的值最小,如果有这样的结点则输出结点的编号。
思路:
以结点1为根,记录每个结点向下的连通度(向上的连通度 = n - 向下的连通度)。
对于其中一个结点 p ,遍历与它有边的结点s1,s2,,,sn,
如果结点 si 的向下的连通度 < 结点 p 向下的连通度,那么这个结点 si 是结点 p 的子树,那么这个结点 si 在删除结点 p 后的连通度 = 这个结点 si 的向下的连通度。
如果结点 si 的向下的连通度 > 结点 p 向下的连通度,那么这个结点 si 是结点 p 的parent,那么这个结点 si 在删除结点 p 后的连通度 = n - 这个结点 si 的向下的连通度。
ps:用vector存树竟然TLE了,要换种结构存树(用链式前向星可以)
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#define DEBUG freopen("_in.txt", "r", stdin); freopen("_out1.txt", "w", stdout);
using namespace std;
const int MAXN = 5e4 + 10;
struct node{
int id, v;
friend bool operator < (const node &a, const node &b){
return (a.v == b.v) ? a.id < b.id : a.v < b.v;
}
}man[MAXN];
int dp[MAXN];
bool vis[MAXN];
int tree[MAXN], ti; //链式前向星
struct road{
int v;
int next;
}R[MAXN<<1];
void addroad(int u, int v){
R[ti].v = v;
R[ti].next = tree[u];
tree[u] = ti++;
R[ti].v = u;
R[ti].next = tree[v];
tree[v] = ti++;
}
void dfs(int p){
vis[p] = true;
dp[p] = 0;
for (int i = tree[p], s, v; i != -1; i = R[i].next){
s = R[i].v;
if (!vis[s]){
dfs(s);
dp[p] = dp[p] + dp[s] + 1; //计算p的向下的连通度
}
}
}
int main(){
int n;
scanf("%d", &n);
memset(tree, -1, sizeof(tree));
for (int i = 1, p, s; i < n; i++){
scanf("%d%d", &p, &s);
addroad(p, s);
}
dfs(1);
int tot = 0;
for (int i = 1, temp; i <= n; i++){
temp = 0;
for (int j = tree[i], s; j != -1; j = R[j].next){
s = R[j].v;
if (dp[s] < dp[i]) //s是i的子树
temp = max(temp, dp[s]+1);
else if (dp[s] > dp[i]) //s是i的parent
temp = max(temp, n-dp[i]-1);
}
man[tot].id = i;
man[tot++].v = temp;
}
sort(man, man+tot);
printf("%d", man[0].id);
for (int i = 1; i < tot && man[i].v == man[i-1].v; i++)
printf(" %d", man[i].id);
return 0;
}