Loading

3210. 【JSOI2013】旅行时的困惑

Description&Data Constraint

喜爱旅游的Will最近在热带岛国Waldives度假。Waldives是由很多岛屿组成的。当地政府希望建立一些新的公交线路(交通工具当然是快艇了),但是却遇到了麻烦。作为计算机科学家的Will自然愿意挺身而出了。

Waldives有 \(N\) 个小岛。目前的交通系统中包含 \(N-1\) 条快艇专线,每条快艇专线连接两个岛。这 \(N-1\) 条快艇专线恰好形成了一棵树。

由于特殊的原因,所有 \(N-1\) 条快艇专线都是单向的。这导致了很多岛屿之间不能相互到达。因此,Waldives政府希望新建一些公交线路,使得建设完毕后,任意两个小岛都可以互相到达。为了节约开支,政府希望建设最少的公交线路。

同时,出于规划考虑,每一条公交线路都有如下的要求:

  1. 每一条交通线路包含若干条连续的快艇专线,你可以认为一条公交线路对应树上的一条路径,而其所包含的若干快艇专线则对应树上被这条路径所覆盖的树边(也就是之前已经存在的某个快艇专线);

  2. 显然一条交通线路只能覆盖树上任意一条边至多一次;

  3. 公交线路中所包含的每一个快艇专线都是有方向的,并且与其所覆盖的树边的方向相反;

  4. 不同的公交线路可以覆盖树上相同的点或者相同的边。

Waldives的 \(N\) 个岛屿分别从 0 到 \(N-1\) 编号。现在给出Waldives已有的快艇专线信息,请计算最少所需要新建的交通线路的数量。

对于 \(100\%\) 的数据满足 \(N\le10^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\n",ans+max(a[1],b[1]));
	return 0;
}
posted @ 2021-10-05 11:19  Thunder_S  阅读(42)  评论(0编辑  收藏  举报