2081.10.04【校内模拟】航班(BCC)(树形DP)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/82937478

【描述】

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

【 输入】

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

【 输出】

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

【 Sample Input】

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

【 Sample Output】

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


解析:

这道题很显然要先缩边双连通分量 ( B C C ) (BCC) ,边双连通里面的边的代价是无效的。

显然一个联通无向图缩完 B C C BCC 之后是一棵树。简单证明一下:
考虑如果不是一棵树,则必然有环(前提是图联通),那么这个环上每个点到其他点都有两条路径,则这个环必然是一个边双连通分量,可以继续缩。

B C C BCC 的方法就是缩 S C C SCC 的方法魔改一下,我就不详细讲了。

然后就直接树形 D P DP ,每个节点维护三个值,最远儿子离他的距离 ( s a n s ) (sans) ,次远儿子离他的距离 ( s u b a n s ) (subans) ,这两个可以在第一次 d f s dfs 搞定。(至于为什么要求次远儿子,马上讲解)。

而第二次 d f s dfs 我们可以处理出经过这个点的父亲的最远点到它的距离,记为 f a n s fans

最后 a n s = m a x ( f a n s , s a n s ) ans=max(fans,sans)

如何更新?
对于根节点,它的所有儿子中离他最远的就是他的答案。
我们这样用所有节点 ( u ) (u) 去更新它的儿子。
如果这个儿子 ( v ) (v) 不是最远儿子则对于这个儿子 f a n s [ v ] = m a x ( f a n s [ u ] , s a n s [ u ] ) + w [ e ] fans[v]=max(fans[u],sans[u])+w[e]
而如果 v v 是最远儿子,则 f a n s [ v ] = m a x ( f a n s [ u ] , s u b a n s [ u ] ) + w [ e ] fans[v]=max(fans[u],subans[u])+w[e] 。因为不能走回头路。

这样就能 A C AC 这道题了。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline
ll getint(){
	re ll num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return num;
}

inline
void outint(ll a){
	static char ch[23];
	if(a==0)pc('0');
	while(a)ch[++ch[0]]=a-a/10*10,a/=10;
	while(ch[0])pc(ch[ch[0]--]^48);
}

cs int N=20004,M=200005;
int n,m;
int last[N],nxt[M<<1],to[M<<1],ecnt;
ll w[M<<1];
inline void addedge(int u,int v,ll val){
	nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v,w[ecnt]=val;
	nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u,w[ecnt]=val;
}

int dfn[N],low[N],dfn_clock;
int scc[N],scc_clock;
int sta[N],top;
bitset<N> insta;
inline void tarjan(int u,int fa){
	dfn[u]=low[u]=++dfn_clock;
	sta[++top]=u;
	insta[u]=true;
	for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
		if(v==fa)continue;
		if(!dfn[v]){
			tarjan(v,u);
			low[u]=min(low[u],low[v]);
		}
		else if(insta[v])low[u]=min(low[u],dfn[v]);
	}
	if(low[u]==dfn[u]){
		++scc_clock;
		re int x=sta[top];
		while(x!=u){
			scc[x]=scc_clock;
			insta[x]=false;
			x=sta[--top];
		}
		--top;
		scc[x]=scc_clock;
		insta[x]=false;
	}
}

namespace TREE{
	int last[N],nxt[N<<1],to[N<<1],ecnt;
	ll w[N<<1];
	inline void addedge(int u,int v,ll val){
		nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v,w[ecnt]=val;
	}
	inline void _addedge(){
		for(int re u=1;u<=n;++u){
			for(int re e=::last[u],v=::to[e];e;v=::to[e=::nxt[e]])
			if(scc[u]!=scc[v])addedge(scc[u],scc[v],::w[e]);
		}
	}
	
	ll ans[N],sans[N],fans[N],subans[N];
	int son[N],subson[N];
	bitset<N> vis;
	inline void dfs1(int u,int fa){
		vis[u]=true;
		for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
			if(v==fa)continue;
			dfs1(v,u);
			re ll res=sans[v]+w[e];
			if(res>sans[u]){
				subson[u]=son[u];
				subans[u]=sans[u];
				son[u]=v,sans[u]=res;
			}
			else if(res>subans[u]){
				subson[u]=v;
				subans[u]=res;
			}
		}
	}
	
	inline void dfs2(int u,int fa){
		ans[u]=max(sans[u],fans[u]);
		for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
			if(v==fa)continue;
			if(v==son[u])fans[v]=max(subans[u],fans[u])+w[e];
			else fans[v]=max(sans[u],fans[u])+w[e];
			dfs2(v,u);
		}
	}
	
	inline void solve(){
		for(int re i=1;i<=:scc_clock;++i){
			if(!vis[i]){
				dfs1(i,0);
				dfs2(i,0);
			}
		}
	}
}

signed main(){
	n=getint();
	m=getint();
	for(int re i=1;i<=m;++i){
		int u=getint(),v=getint();
		ll val=getint();
		addedge(u,v,val);
	}
	for(int re i=1;i<=n;++i)if(!dfn[i])tarjan(i,0);
	TREE::_addedge();
	TREE::solve();
	for(int re i=1;i<=n;++i)outint(TREE::ans[scc[i]]),pc('\n');
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/82937478
今日推荐