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














posted @ 2024-11-29 11:32  Glowingfire  阅读(1)  评论(0编辑  收藏  举报