P3761 [TJOI2017]城市
Posted CCSU_Cola
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3761 [TJOI2017]城市相关的知识,希望对你有一定的参考价值。
从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作。这个地区一共有 n 座城市,n-1 条高速公路,保证了任意两运城市之间都可以通过高速公路相互可达,但是通过一条高速公路需要收取一定的交通费用。小明对这个地区深入研究后,觉得这个地区的交通费用太贵。
小明想彻底改造这个地区,但是由于上司给他的资源有限,因而小明现在只能对一条高速公路进行改造,改造的方式就是去掉一条高速公路,并且重新修建一条一样的高速公路(即交通费用一样),使得这个地区的两个城市之间的最大交通费用最小(即使得交通费用最大的两座城市之间的交通费用最小),并且保证修建完之后任意两座城市相互可达。如果你是小明,你怎么解决这个问题?
题意:从树上删除一条边,然后建一条新边,边权为删掉的边的边权,使得仍然是一棵树,且使得树的直径最短,输出最短的直径的答案。
思路:既然要使得直径最短,那么先从未改变的树入手,如果删除的不是树的直径上的边,那么答案只可能变大,因为之前直径的值没变,那么为了将直径变小,我们可以采用依次删除直径上的边的方法来得到答案,那既然删除了边,应该连到哪里最合适,肯定是连接两个连通块的中心获得的答案最小,那么可以分别跑出两个连通块的中心到离它最远点的距离加上新建边的边权与两个连通块的直径取max得到答案即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
struct tt
int x,to,val;
;
tt e[1000010];
int idx=0,h[5010];
void add(int a,int b,int c)
e[idx].x=b,e[idx].val=c,e[idx].to=h[a],h[a]=idx++;
int ans,pos;
int vis[5010];//标记重心
int st[5010];
int flag[1000010];//标记直径上路线
int n;
vector<int>ve;
void dfs(int x,int fa,int step)
if(step>ans)
ans=step;
pos=x;
for(int i=h[x];i!=-1;i=e[i].to)
if(flag[i])continue;
int j=e[i].x;
if(j==fa)continue;
dfs(j,x,step+e[i].val);
void ddfs(int x,int fa)
for(int i=h[x];i!=-1;i=e[i].to)
int j=e[i].x;
if(j==fa)continue;
ddfs(j,x);
vis[x]|=vis[j];
void find(int x,int fa)
for(int i=h[x];i!=-1;i=e[i].to)
int j=e[i].x;
if(j==fa)continue;
if(vis[x]&&vis[j])ve.push_back(i);
find(j,x);
const int INF=2e9;
int d1[5010],d2[5010],p1[5010],up[5010],ac[5010];
void get_center(int x,int fa)
d1[x]=d2[x]=-INF;
st[x]=1;
ac[x]=1;
for(int i=h[x];i!=-1;i=e[i].to)
if(flag[i])continue;
int j=e[i].x;
if(j==fa)continue;
get_center(j,x);
ll len=d1[j]+e[i].val;
if(len>d1[x])
d2[x]=d1[x];
d1[x]=len;
p1[x]=j;
else if(len>d2[x])
d2[x]=len;
if(d1[x]==-INF)d1[x]=d2[x]=0;
void dfss(int x,int fa)
for(int i=h[x];i!=-1;i=e[i].to)
if(flag[i])continue;
int j=e[i].x;
if(j==fa)continue;
if(p1[x]==j)up[j]=max(up[x],d2[x])+e[i].val;
else up[j]=max(up[x],d1[x])+e[i].val;
dfss(j,x);
int solve(int val)
memset(st,0,sizeof st);
int flag1=1;
int mi1=2e9,mi2=2e9,l1,l2;
int pos1=-1,pos2=-1;
for(int i=1;i<=n;i++)
if(!st[i])
memset(up,0,sizeof up);
memset(p1,0,sizeof p1);
ans=-1;
dfs(i,-1,0);
ans=-1;
dfs(pos,-1,0);
get_center(i,-1);
dfss(i,-1);
if(flag1)
for(int i=1;i<=n;i++)
if(ac[i])
mi1=min(mi1,max(d1[i],up[i]));//距离中心最远点的距离
l1=ans;//存储连通块的直径的长度
memset(ac,0,sizeof ac);
else
for(int i=1;i<=n;i++)
if(ac[i])
mi2=min(mi2,max(d1[i],up[i]));//距离中心最远点的距离
l2=ans;//存储连通块的直径的长度
memset(ac,0,sizeof ac);
flag1=0;
return max(l1,max(l2,mi1+mi2+val));
int main()
memset(h,-1,sizeof h);
scanf("%d",&n);
int a,b,c;
for(int i=1;i<n;i++)
scanf("%d%d%d",&a,&b,&c);
add(a,b,c),add(b,a,c);
ans=-1;
dfs(1,-1,0);//找最远点
int pp=pos;
ans=-1;
dfs(pos,-1,0);//找直径的另一个点
int res=1e9;
vis[pp]=1;
ddfs(pos,-1);//标记直径上的点
find(pos,-1);//存储直径上的边
for(int i=0;i<ve.size();i++)
flag[ve[i]]=1;//标记直径上的边
flag[ve[i]^1]=1;//标记直径上的边
if(i!=0)flag[ve[i-1]]=0,flag[ve[i-1]^1]=0;//还原上一次标的边
int f=solve(e[ve[i]].val);//求答案
res=min(f,res);
printf("%d\\n",res);
以上是关于P3761 [TJOI2017]城市的主要内容,如果未能解决你的问题,请参考以下文章