Description&Data Constraint
喜爱旅游的Will最近在热带岛国Waldives度假。Waldives是由很多岛屿组成的。当地政府希望建立一些新的公交线路(交通工具当然是快艇了),但是却遇到了麻烦。作为计算机科学家的Will自然愿意挺身而出了。
Waldives有 (N) 个小岛。目前的交通系统中包含 (N-1) 条快艇专线,每条快艇专线连接两个岛。这 (N-1) 条快艇专线恰好形成了一棵树。
由于特殊的原因,所有 (N-1) 条快艇专线都是单向的。这导致了很多岛屿之间不能相互到达。因此,Waldives政府希望新建一些公交线路,使得建设完毕后,任意两个小岛都可以互相到达。为了节约开支,政府希望建设最少的公交线路。
同时,出于规划考虑,每一条公交线路都有如下的要求:
-
每一条交通线路包含若干条连续的快艇专线,你可以认为一条公交线路对应树上的一条路径,而其所包含的若干快艇专线则对应树上被这条路径所覆盖的树边(也就是之前已经存在的某个快艇专线);
-
显然一条交通线路只能覆盖树上任意一条边至多一次;
-
公交线路中所包含的每一个快艇专线都是有方向的,并且与其所覆盖的树边的方向相反;
-
不同的公交线路可以覆盖树上相同的点或者相同的边。
Waldives的 (N) 个岛屿分别从 0 到 (N-1) 编号。现在给出Waldives已有的快艇专线信息,请计算最少所需要新建的交通线路的数量。
对于 (100\%) 的数据满足 (Nle10^5) 。
Solution
可以证明,如果一条公交线路可以在子树内建成,就不会在往子树外走。
简单的证明:如果当前子树内有可以连的线路,如果往外走,要么与当前连边数一致,要么多连,答案不会更优。
因此考虑贪心。设 (a_i) 和 (b_i) 表示父亲要走过来多少和要往父亲走多少。
对于当前节点 (x),它的子节点 (son) 只有 (a_i) 和 (b_i) 中的一个可以传递到 (x),那么另一个就可以记录答案。
另外,对于节点 (x),优先让其在内部建线路,剩余的再传递上去。
Code
#include<cstdio>
#include<algorithm>
#define N 100005
using namespace std;
struct ndoe
{
int to,next,head,fx;
}tree[N<<1];
int n,x,y,ans,tot,a[N],b[N];
void add(int x,int y,int opt)
{
tree[++tot].to=y;
tree[tot].fx=opt;
tree[tot].next=tree[x].head;
tree[x].head=tot;
}
void dfs(int x,int fa)
{
for (int i=tree[x].head;i;i=tree[i].next)
{
int v=tree[i].to;
if (v==fa) continue;
dfs(v,x);
if (tree[i].fx)//看能传递哪一部分,另外一部分计入答案
{
ans+=a[v];
b[x]+=max(b[v],1);
}
else
{
ans+=b[v];
a[x]+=max(a[v],1);
}
}
int t=min(a[x],b[x]);
a[x]-=t;b[x]-=t;ans+=t;//优先在内部连边
}
int main()
{
scanf("%d",&n);
for (int i=1;i<n;++i)
{
scanf("%d%d",&x,&y);
add(x+1,y+1,1);add(y+1,x+1,0);
}
dfs(1,0);
printf("%d
",ans+max(a[1],b[1]));
return 0;
}