题目描述
$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++
原文地址:https://www.cnblogs.com/wzc521/p/11577353.html