[CSP-S模拟测试]:联盟(搜索+树的直径)

题目描述

$G$国周边的$n$个小国家构成一个联盟以抵御$G$国入侵,为互相支援,他们建立了$n−1$条双向通路,使得任意两个国家可以经过通路相互到达。
当一个国家受到攻击时,所有其它国家都会沿着最短路径前往这个国家进行支援,经过每条通路所需的时间均为$1$。定义一个国家的危险程度为所有国家全部赶到需要的最短时间,联盟的危险程度为所有国家的危险程度的最大值。
为了降低危险程度,联盟决定断开一条通路并任意连接一条通路,使得危险程度尽可能小,并要求改建完成之后任意两个国家可以经过通路互相到达。他们决定让你来设计方案,你需要告知在最优方案中可能断开哪些边,并给出任意一组最优方案。


输出格式

第一行一个正整数$n$。
接下来$n−1$行每行两个正整数,表示一条$u_i,v_i$之间的边。


输出格式

输出第一行一个整数表示最小危险程度。
第二行一个整数$k$,表示可能被断开的边的数量,接下来$k$个数,表示可能断开的边的编号,按升序输出。
接下来一行四个正整数表示一组最优方案,分别表示断开和新建的边的端点,只需给出任意一组合法的方案即可。


样例

样例输入:

4
1 2
2 3
3 4

样例输出:

2
2 1 3
3 4 4 2


数据范围与提示

对于$20\%$的数据,$n\leqslant 30$。
对于$40\%$的数据,$n\leqslant 300$。
对于$60\%$的数据,$n\leqslant 3,000$。
对于$100\%$的数据,$n\leqslant 300,000$。

如果你的答案仅第一行正确,你可以获得$25\%$的分数,
如果你的答案仅前两行正确,你可以获得$50\%$的分数,
为保证得到部分分请确保提交程序的输出格式符合题目要求。


题解

联盟的危险程度其实就是树的直径,这个我们使用两遍$DFS$即可解决。

然后我们还可以用两遍$DFS$求出每棵子树的直径。

现在我们枚举断边,至于如何连边,显然我们是要将两个联通块中直径的中点相连一定最优。

这时候我们已经知道第一问和第二问了。

第三问在跑$DFS$就解决了。

时间复杂度:$\Theta(n)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
struct rec{int nxt,to;}e[700000];
int head[300001],cnt=1;
int n;
int len,st,ed,ban;
int dis[2][300001];
int dfn[300001],low[300001];
int dp[2][300001];
int ans[300001],sum;
void add(int x,int y)
{
	e[++cnt].nxt=head[x];
	e[cnt].to=y;
	head[x]=cnt;
}
void dfs1(int x,int fa,int tim)
{
	if(tim>=len)
	{
		len=tim;
		st=x;
	}
	for(int i=head[x];i;i=e[i].nxt)
		if(e[i].to!=fa&&(ban!=i)&&((ban^1)!=i))dfs1(e[i].to,x,tim+1);
}
void dfs2(int x,int fa,int tim)
{
	if(tim>=len)
	{
		len=tim;
		ed=x;
	}
	for(int i=head[x];i;i=e[i].nxt)
		if(e[i].to!=fa&&(ban!=i)&&((ban^1)!=i))
		{
			dfs2(e[i].to,x,tim+1);
			dis[1][i>>1]=e[i].to;
		}
}
bool dfs3(int x,int fa)
{
	dfn[++dfn[0]]=x;
	if(x==ed)return 1;
	for(int i=head[x];i;i=e[i].nxt)
		if(e[i].to!=fa&&(ban!=i)&&((ban^1)!=i)&&dfs3(e[i].to,x))
		{
			low[i>>1]=1;
			return 1;
		}
	dfn[0]--;
	return 0;
}
int dfs4(int x,int fa)
{
	int maxn=0;
	for(int i=head[x];i;i=e[i].nxt)
		if(e[i].to!=fa)
		{
			int flag=dfs4(e[i].to,x);
			dp[0][x]=max(dp[0][x],dp[0][e[i].to]);
			dp[0][x]=max(dp[0][x],flag+maxn);
			maxn=max(maxn,flag);
		}
	return maxn+1;
}
int dfs5(int x,int fa)
{
	int maxn=0;
	for(int i=head[x];i;i=e[i].nxt)
		if(e[i].to!=fa)
		{
			int flag=dfs5(e[i].to,x);
			dp[1][x]=max(dp[1][x],dp[1][e[i].to]);
			dp[1][x]=max(dp[1][x],flag+maxn);
			maxn=max(maxn,flag);
		}
	return maxn+1;
}
int main()
{
	ans[0]=1<<30;
	scanf("%d",&n);
	for(int i=1;i<n;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);add(y,x);
	}
	dfs1(1,0,0);
	len=0;
	dfs2(st,0,0);
	dfs3(st,0);
	dfs4(st,0);
	dfs5(ed,0);
	for(int i=1;i<n;i++)
	{
		if(e[i<<1].to==dis[1][i])dis[0][i]=e[i<<1|1].to;
		else dis[0][i]=e[i<<1].to;
	}
	for(int i=1;i<n;i++)
		if(!low[i])
		{
			ans[i]=max((len+1)/2+min(dp[0][e[i<<1].to]+1,dp[0][e[i<<1|1].to]+1)/2+1,len);
			ans[0]=min(ans[0],ans[i]);
		}
		else
		{
			ans[i]=max(max(dp[0][dis[1][i]],dp[1][dis[0][i]]),(dp[0][dis[1][i]]+1)/2+(dp[1][dis[0][i]]+1)/2+1);
			ans[0]=min(ans[0],ans[i]);
		}
	printf("%d\n",ans[0]);
	for(int i=1;i<n;i++)if(ans[i]==ans[0])sum++;
	printf("%d ",sum);
	for(int i=1;i<n;i++)if(ans[i]==ans[0])printf("%d ",i);
	puts("");
	for(int i=1;i<n;i++)
		if(ans[i]==ans[0])
		{
			printf("%d %d ",e[i<<1].to,e[i<<1|1].to);
			ban=i<<1;
			len=dfn[0]=0;
			dfs1(e[ban].to,0,0);
			len=0;
			dfs2(st,0,0);
			dfs3(st,0);
			printf("%d ",dfn[dfn[0]+1>>1]);
			len=dfn[0]=0;
			dfs1(e[ban^1].to,0,0);
			len=0;
			dfs2(st,0,0);
			dfs3(st,0);
			printf("%d ",dfn[dfn[0]+1>>1]);
			break;
		}
	return 0;
}

rp++

posted @ 2019-09-24 11:32  HEOI-动动  阅读(227)  评论(0编辑  收藏  举报