【2018/10/04】T3 - 边双连通分量+树形dp - 航班

航班

描述

L因为业务繁忙,经常会到处出差。因为他是航空公司的优质客户,于是某个航空公司给了他一个优惠券。

他可以利用这个优惠券在任何一个国家内的任意城市间免费旅行,当他的路线跨国才会产生费用。L有一个航空公司的价格表与航线。而且每个城市出发都能到所有的城市,2个城市间可能有不止一个航班,一个国家内的2个城市间一定有不同的路线,但是不同国家的城市间只有一条路线。L想知道从每个城市出发到产生费用最多的城市,不过你不能重复在一个航班上飞来飞去产生费用,必须沿最少的费用路线飞行

输入

第一行,两个整数 N,M,表示N 个城市, M 条航线。

接下来 M 行,每行三个整数 a,b,c,表示城市 a,b 之间有一条费用为 c 的航线。

输出

共 N 行,第 i 行为从城市 i 出发到达每个城市额外费用的最大值。

样例输入

6 6
1 4 2
1 2 6
2 5 3
2 3 7
6 3 4
3 1 8

样例输出

4
4
4
6
7
7

 

【解释】

有四个国家,包含的城市分别为 {1,2,3},{4},{5},{6}。

从城市 1 出发到达城市 6,乘坐(1,3)(3,6)两个航班费用最大,(1,3)在国内为免费航班, (3,6)的费用为 4,所以从 1 出发的最大费用为 4。

【数据规模】

对于 40%的数据 1<=N<=1000,1<=M<=1000

对于 100%的数据 1<=N<=20000,1<=M<=200000

 

分析

这道题还是很显然的,“一个国家内的2个城市间一定有不同的路线”明显的边双连通,就可以用tarjan缩点了,缩完后就是一颗树

“L想知道从每个城市出发到产生费用最多的城市,不过你不能重复在一个航班上飞来飞去产生费用,必须沿最少的费用路线飞行”

由于最后缩完点就是一颗树,点与点的路径唯一确定,就不用考虑那个什么最少费用了

 两次dfs/树形dp求直径的2个端点A,B,则 x到最远距离的距离是 MAX(dis(x,A),dis(x,B)) 

这个证明很显然。。。。

抱歉我不应该用显然这个词。。这个证明可以反证出来,嗯,下面上代码

 

代码

#include<bits/stdc++.h>
#define N 20009
#define M 500009
#define in read()
using namespace std;
int n,m;
inline int read(){每次代码一长,我就想把读优省略}
int nxt[M],head[N],to[M],d[M],cnt=1;
void add(int x,int y,int w){
	nxt[++cnt]=head[x];head[x]=cnt;to[cnt]=y;d[cnt]=w;
	nxt[++cnt]=head[y];head[y]=cnt;to[cnt]=x;d[cnt]=w;
}
int dfn[N],low[N],dfs=0,be[N],num=0;
bool insta[N];
stack<int > s;
void tarjan(int x,int from){//和有向图的强连通分量唯一不同就在于要记录一下来的边
	s.push(x);insta[x]=1;
	dfn[x]=low[x]=++dfs;
	for(int e=head[x];e;e=nxt[e]){
		int y=to[e];
		if(!dfn[y]){
			tarjan(y,e);
			low[x]=min(low[x],low[y]);
		}
		else if(e!=(from^1)&&insta[y]) low[x]=min(low[x],dfn[y]);//注意优先级哦(from^1)
	}
	int u;
	if(dfn[x]==low[x]){
		++num;
		do{
			u=s.top();s.pop();
			insta[u]=0;
			be[u]=num;
		}while(u!=x);
	}
}
int nxtt[M],headd[N],too[M],dd[M],cntt=1;
void new_add(int x,int y,int w){nxtt[++cntt]=headd[x];headd[x]=cntt;too[cntt]=y;dd[cntt]=w;}
int dis[N],roota,rootb,maxn=-1,disa[N],disb[N];//roota,rootb分别表示直径的一个端点
void dfs1(int u,int fu)
{
	for(int e=headd[u];e;e=nxtt[e]){
		int v=too[e];
		if(v==fu) continue;
		dis[v]=dis[u]+dd[e];
		if(dis[v]>maxn) maxn=dis[v],roota=v;
		dfs1(v,u);
	}
}
void dfs2(int u,int fu){
	for(int e=headd[u];e;e=nxtt[e]){
		int v=too[e];
		if(v==fu) continue;
		disa[v]=disa[u]+dd[e];
		dfs2(v,u);
	}
}//其实dfs2是可以和dfs1合并的,而且可以直接用dis来表示disa,就可以啦
void dfs3(int u,int fu){
	for(int e=headd[u];e;e=nxtt[e]){
		int v=too[e];
		if(v==fu) continue;
		disb[v]=disb[u]+dd[e];
		dfs3(v,u);
	}	
}
int main(){
	n=in;m=in;
	int a,b,c;
	while(m--){
		a=in;b=in;c=in;
		add(a,b,c);
	}
	for(int i=1;i<=n;++i){	if(!dfn[i]) tarjan(i,0);}
	for(int i=1;i<=n;++i){
		for(int e=head[i];e;e=nxt[e]){
			int j=to[e];
			if(be[i]==be[j]) continue;
			new_add(be[i],be[j],d[e]);
		}
	}
	dis[1]=0;dfs1(1,0);
	maxn=-1;dis[roota]=0;rootb=roota;
	dfs1(roota,0);
	dfs2(roota,0);dfs3(rootb,0);
	for(int i=1;i<=n;++i){
		if(disa[be[i]]>disb[be[i]]) printf("%d\n",disa[be[i]]);
		else printf("%d\n",disb[be[i]]);
	}
	return 0;
}

树形dp版本的,今晚就会补上来啦

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值