1. 程式人生 > >NOIP模擬 航班(scc強連通分量+樹形DP/雙Dfs)

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;
}