NOIP模擬 航班(scc強連通分量+樹形DP/雙Dfs)
阿新 • • 發佈:2018-12-13
【題目描述】
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;
}