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