P2573 [SCOI2012] 滑雪 —— 生成树
[SCOI2012] 滑雪
题目描述
a180285 非常喜欢滑雪。他来到一座雪山,这里分布着 \(m\) 条供滑行的轨道和 \(n\) 个轨道之间的交点(同时也是景点),而且每个景点都有一编号 \(i\space (1 \le i \le n)\) 和一高度 \(h_i\)。
a180285 能从景点 \(i\) 滑到景点 \(j\) 当且仅当存在一条 \(i\) 和 \(j\) 之间的边,且 \(i\) 的高度不小于 \(j\)。与其他滑雪爱好者不同,a180285 喜欢用最短的滑行路径去访问尽量多的景点。如果仅仅访问一条路径上的景点,他会觉得数量太少。
于是 a18028 5拿出了他随身携带的时间胶囊。这是一种很神奇的药物,吃下之后可以立即回到上个经过的景点(不用移动也不被认为是 a180285 滑行的距离)。
请注意,这种神奇的药物是可以连续食用的,即能够回到较长时间之前到过的景点(比如上上个经过的景点和上上上个经过的景点)。 现在,a180285站在 \(1\) 号景点望着山下的目标,心潮澎湃。他十分想知道在不考虑时间胶囊消耗的情况下,以最短滑行距离滑到尽量多的景点的方案(即满足经过景点数最大的前提下使得滑行总距离最小)。你能帮他求出最短距离和景点数吗?
输入格式
输入的第一行是两个整数 \(n,m\)。
接下来一行有 \(n\) 个整数 \(h_i\),分别表示每个景点的高度。
接下来 \(m\) 行,表示各个景点之间轨道分布的情况。每行三个整数 \(u,v,k\),表示编号为 \(u\) 的景点和编号为 \(v\) 的景点之间有一条长度为 \(k\) 的轨道。
输出格式
输出一行,表示 a180285 最多能到达多少个景点,以及此时最短的滑行距离总和。
样例 #1
样例输入 #1
3 3
3 2 1
1 2 1
2 3 1
1 3 10
样例输出 #1
3 2
提示
【数据范围】
对于 $ 30% $ 的数据,$ 1 \le n \le 2000 $;
对于 $ 100% $ 的数据,$ 1 \le n \le 10^5 $。
对于所有的数据,保证 $ 1 \le m \le 10^6 $ , $ 1 \le h_i \le 10^9 $ ,$ 1 \le k_i \le 10^9 $。
分析
先从1号点出发搜索所有能遍历的点,顺便记下遍历过的边。
以终点的高度为第一关键字从大到小,以边的长度为第二关键字从小到大,对标记的边排序。
前者保证能遍历尽可能多的点,后者保证最小距离。
合并边到新的生成树上即可。
注意同样高度的点建双向边。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+100,M=1e6+100;
struct edge{int y,n,x;long long z;}e[M<<1],t[M<<1];
int n,m,h[N],f[N];
int tot,cnt,head[N],sum;
int dh[N],rec[N],q[M];
bool vis[N];
long long dis;
bool cmp(edge a,edge b)
{
if(h[a.y]==h[b.y])return a.z<b.z;
return h[a.y]>h[b.y];
}
int fin(int x)
{
if(f[x]==x)return x;
f[x]=fin(f[x]);
return f[x];
}
void bfs()
{
int he=1,ta=0;
q[++ta]=1;sum=1;vis[1]=1;
while(he<=ta)
{
int u=q[he];
++he;
for(int i=head[u];i;i=e[i].n)
{
int v=e[i].y;
t[++cnt].n=dh[u];
t[cnt].y=v;
t[cnt].z=e[i].z;
t[cnt].x=u;
dh[u]=cnt;
if(!vis[v])
{
vis[v]=1;
++sum;
q[++ta]=v;
}
}
}
}
void go(int u,int fa,long long len)
{
bool fl=0;
for(int i=dh[u];i;i=t[i].n)
{
int v=t[i].y;
if(v==fa)continue;
go(v,u,len+t[i].z);
fl=1;
}
if(!fl)dis+=len;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){f[i]=i;scanf("%d",h+i);}
for(int i=1,x,y;i<=m;++i)
{
long long z=0;
scanf("%d%d%lld",&x,&y,&z);
if(h[x]<h[y])swap(x,y);// x --> y
e[++tot].n=head[x];
e[tot].x=x;
e[tot].y=y;
e[tot].z=z;
head[x]=tot;
if(h[x]==h[y])
{
e[++tot].n=head[y];
e[tot].x=y;
e[tot].y=x;
e[tot].z=z;
head[y]=tot;
}
}
bfs();
sort(t+1,t+1+cnt,cmp);
int num=0;
for(int i=1,x,y,fx,fy;i<=cnt;++i)
{
x=t[i].x;
y=t[i].y;
fx=fin(x);
fy=fin(y);
if(fx!=fy)
{
f[fy]=fx;
dis+=t[i].z;
++num;
if(num==sum-1)break;
}
}
printf("%d %lld",sum,dis);
return 0;
}
本文来自博客园,作者:Glowingfire,转载请注明原文链接:https://www.cnblogs.com/Glowingfire/p/18576236