题面
题解
首先我们要思考一个问题, 公交线路要加到什么程度才能够使得任意两个点两两可达
很容易知道每个点和他的父亲必须两两可达
所以最终所有公交线路新增的边其实就是把原来的边全部反向
那么这些边要如何组成公交线路呢, 必须要满足, 在这条公交线路中, 边的指向相同, 要么都向上, 要么都向下, 或者原来向上, 在某个点转成向下, 也可以反过来
我们既然要使得公交线路数最短, 就要使只向上的和只向下的路径长度尽量长, 并且要是更多的向上和向下的路径在某个点相交
那么我们就可以得到这样一个贪心策略
对于一个点, 我们先尽量让向上的和向下的相抵消, 那么最多一种会剩下来, 然后再看这一种和这个点向父亲连的边的方向是不是一样的
如果是, 就把他传上去, 看能不能再跟其他的匹配
如果不是, 那他传不了了, 就只能到这里打止
这样一定是最优的, 如果你当前不把能匹配的匹配完了, 上去匹配的话, 可能没有可以和你匹配的
但如果你在这里就匹配了的话, 公交线路数量至少不会变多
所以这样是最优的
Code
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
const int N = 100005;
using namespace std;
int n, st[N], ed[N], head[N], ans, g[N], up[N], down[N], cnt, dep[N], id[N];
struct edge { int to, nxt; } e[N << 1];
template < typename T >
inline T read()
{
T x = 0, w = 1; char c = getchar();
while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * w;
}
inline void adde(int u, int v) { e[++cnt] = (edge) { v, head[u] }, head[u] = cnt; }
void dfs1(int u, int fa)
{
dep[u] = dep[fa] + 1;
for(int v, i = head[u]; i; i = e[i].nxt)
{
v = e[i].to; if(v == fa) continue;
dfs1(v, u);
}
}
void dfs(int u, int fa)
{
for(int v, i = head[u]; i; i = e[i].nxt)
{
v = e[i].to; if(v == fa) continue;
dfs(v, u); id[v] == 1 ? down[u] += g[v] : up[u] += g[v];
}
int tmp = min(down[u], up[u]);
ans += tmp, down[u] -= tmp, up[u] -= tmp;
if(u == 1) ans += up[u] + down[u];
else ans += id[u] == 1 ? up[u] : down[u];
g[u] = max(1, id[u] == 1 ? down[u] : up[u]);
}
int main()
{
n = read <int> ();
for(int i = 1; i < n; i++)
{
st[i] = read <int> () + 1, ed[i] = read <int> () + 1;
adde(st[i], ed[i]), adde(ed[i], st[i]);
}
dfs1(1, 0);
for(int i = 1; i < n; i++)
if(dep[st[i]] < dep[ed[i]])
id[ed[i]] = 1;
else id[st[i]] = 2;
dfs(1, 0);
printf("%d
", ans);
return 0;
}