NOIP模拟 航班(scc强连通分量+树形DP/双Dfs)

【题目描述】

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

【备注】

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

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

【题目分析】

首先,由于同一国家的城市之间并不需要花费,所以联想到tarjan缩点还是很容易的。

缩点完成后,根据国家与国家之间的航班建立一张图,由于题上说了不同国家的城市间只有一条路线,所以最后形成的图可以明显发现是一棵树。可以证明,所求答案为该点到所得树的直径两端点的较大值,由下图可以证明:

所以就有两种做法:Dfs两次或者树形DP。

Dfs:先dfs一次找到一个端点,第二次dfs找到所有点到该端点的距离,得到另一个端点,再做一次即可。

树形DP:记录树的每个节点的最大值和最小值,从儿子推一遍,再从父亲推一遍即可。

【代码~】

Dfs:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e4+10;
const int MAXM=4e5+10;
const int INF=0x3f3f3f3f;

int n,m;
int dis[MAXN],diss[MAXN];
int inde;
int dfn[MAXN],low[MAXN];
bool insta[MAXN];
int belong[MAXN],tot;
int sta[MAXN],top;

int Read()
{
	int i=0,f=1;
	char c;
	for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
	if(c=='-')
	  f=-1,c=getchar();
	for(;c>='0'&&c<='9';c=getchar())
	  i=(i<<3)+(i<<1)+c-'0';
	return i*f;
}

int cnt,cntt;
int head[MAXN],headd[MAXN];
int nxt[MAXM],nxtt[MAXM],to[MAXM],too[MAXM],w[MAXM],ww[MAXM];

void add(int x,int y,int z)
{
	cnt++;
	nxt[cnt]=head[x];
	head[x]=cnt;
	to[cnt]=y;
	w[cnt]=z;
}

void Add(int x,int y,int z)
{
	cnt++;
	nxtt[cnt]=headd[x];
	headd[x]=cnt;
	too[cnt]=y;
	ww[cnt]=z;
}

void tarjan(int u,int fa)
{
	dfn[u]=low[u]=++inde;
	insta[u]=true;
	sta[++top]=u;
	for(int i=head[u];i!=-1;i=nxt[i])
	{
		int v=to[i];
		if(v==fa)
		  continue;
		if(!dfn[v])
		{
			tarjan(v,u);
			if(low[v]<low[u])
			  low[u]=low[v];
		}
		else
		{
			if(insta[v]&&dfn[v]<low[u])
			  low[u]=dfn[v];
		}
	}
	int j;
	if(dfn[u]==low[u])
	{
		tot++;
		do
		{
			j=sta[top--];
			insta[j]=false;
			belong[j]=tot;
		}while(j!=u);
	}
}


int mx,root;
void dfs1(int u,int fa)
{
	for(int i=headd[u];i!=-1;i=nxtt[i])
	{
		int v=too[i];
		if(v==fa)
		  continue;
		dis[v]=dis[u]+ww[i];
		if(dis[v]>mx)
		  mx=dis[v],root=v;
		dfs1(v,u);
	}
}

void dfs2(int u,int fa)
{
	for(int i=headd[u];i!=-1;i=nxtt[i])
	{
		int v=too[i];
		if(v==fa)
		  continue;
		diss[v]=diss[u]+ww[i];
		dfs2(v,u);
	}
}

void solve1()
{
	memset(head,-1,sizeof(head));
	memset(nxt,-1,sizeof(nxt));
	for(int i=1;i<=m;++i)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z),add(y,x,z);
	}
	for(int i=1;i<=n;++i)
	  if(!dfn[i])
	    tarjan(i,0);
}

void solve2()
{
	cnt=0;
	memset(headd,-1,sizeof(headd));
	memset(nxtt,-1,sizeof(nxtt));
	for(int i=1;i<=n;++i)
	{
		for(int j=head[i];j!=-1;j=nxt[j])
		{
			int v=to[j];
			if(belong[i]!=belong[v])
			  Add(belong[i],belong[v],w[j]);
		}
	}
	dfs1(1,-1);
	mx=0;
	memset(dis,0,sizeof(dis));
	dfs1(root,-1);
	mx=0;
	dfs2(root,-1);
	for(int i=1;i<=n;++i)
	  printf("%d\n",max(dis[belong[i]],diss[belong[i]]));
}

int main()
{
	n=Read(),m=Read();
	solve1();
	solve2();
	return 0;
}

树形DP(变量名调的蛋疼):

#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e4+10;
const int MAXM=4e5+10;
const int INF=0x3f3f3f3f;

int n,m;
int dis[MAXN],diss[MAXN];
int inde;
int dfn[MAXN],low[MAXN];
bool insta[MAXN];
int belong[MAXN],tot;
int sta[MAXN],top;

int Read()
{
	int i=0,f=1;
	char c;
	for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
	if(c=='-')
	  f=-1,c=getchar();
	for(;c>='0'&&c<='9';c=getchar())
	  i=(i<<3)+(i<<1)+c-'0';
	return i*f;
}

int cnt,cntt;
int head[MAXN],headd[MAXN];
int nxt[MAXM],nxtt[MAXM],to[MAXM],too[MAXM],w[MAXM],ww[MAXM];

void add(int x,int y,int z)
{
	cnt++;
	nxt[cnt]=head[x];
	head[x]=cnt;
	to[cnt]=y;
	w[cnt]=z;
}

void Add(int x,int y,int z)
{
	cnt++;
	nxtt[cnt]=headd[x];
	headd[x]=cnt;
	too[cnt]=y;
	ww[cnt]=z;
}

void tarjan(int u,int fa)
{
	dfn[u]=low[u]=++inde;
	insta[u]=true;
	sta[++top]=u;
	for(int i=head[u];i!=-1;i=nxt[i])
	{
		int v=to[i];
		if(v==fa)
		  continue;
		if(!dfn[v])
		{
			tarjan(v,u);
			if(low[v]<low[u])
			  low[u]=low[v];
		}
		else
		{
			if(insta[v]&&dfn[v]<low[u])
			  low[u]=dfn[v];
		}
	}
	int j;
	if(dfn[u]==low[u])
	{
		tot++;
		do
		{
			j=sta[top--];
			insta[j]=false;
			belong[j]=tot;
		}while(j!=u);
	}
}

void solve1()
{
	memset(head,-1,sizeof(head));
	memset(nxt,-1,sizeof(nxt));
	for(int i=1;i<=m;++i)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z),add(y,x,z);
	}
	for(int i=1;i<=n;++i)
	  if(!dfn[i])
	    tarjan(i,0);
}

int f[MAXN],s[MAXN],cd[MAXN],S[MAXN];
void dfs(int u,int fa)
{
	for(int i=headd[u];i!=-1;i=nxtt[i])
	{
		int v=too[i];
		if(v==fa)
		  continue;
		dfs(v,u);
		if(s[v]+ww[i]>s[u])
		{
			cd[u]=s[u];
			s[u]=s[v]+ww[i];
			S[u]=v;
		}
		else
		{
			cd[u]=max(cd[u],s[v]+ww[i]);
		}
	}
}

void DFS(int u,int fa)
{
	for(int i=headd[u];i!=-1;i=nxtt[i])
	{
		int v=too[i];
		if(v==fa)
		  continue;
		if(v==S[u])
		{
			f[v]=max(f[u],cd[u])+ww[i];
		}
		else
		  f[v]=max(s[u],f[u])+ww[i];
		DFS(v,u);
	}
}

void solve2()
{
	cnt=0;
	memset(headd,-1,sizeof(headd));
	memset(nxtt,-1,sizeof(nxtt));
	for(int i=1;i<=n;++i)
	{
		for(int j=head[i];j!=-1;j=nxt[j])
		{
			int v=to[j];
			if(belong[i]!=belong[v])
			  Add(belong[i],belong[v],w[j]);
		}
	}
	dfs(1,-1);
	DFS(1,-1);
	for(int i=1;i<=n;++i)
	  printf("%d\n",max(f[belong[i]],s[belong[i]]));
}

int main()
{
	n=Read(),m=Read();
	solve1();
	solve2();
	return 0;
}

 

转载于:https://www.cnblogs.com/Ishtar/p/10010837.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值