1. 程式人生 > >【最小樹形圖(奇怪的kruskal)】【SCOI 2012】【bzoj 2753】滑雪與時間膠囊

【最小樹形圖(奇怪的kruskal)】【SCOI 2012】【bzoj 2753】滑雪與時間膠囊

方案 track solved views end cmp ren scoi2012 ext

2753: [SCOI2012]滑雪與時間膠囊

Time Limit: 50 Sec  Memory Limit: 128 MB
Submit: 1621  Solved: 570

Description

a180285非常喜歡滑雪。

他來到一座雪山,這裏分布著M條供滑行的軌道和N個軌道之間的交點(同一時候也是景點)。並且每一個景點都有一編號i(1<=i<=N)和一高度Hi。a180285能從景點i 滑到景點j 當且僅當存在一條i 和j 之間的邊,且i 的高度不小於j。

與其它滑雪愛好者不同,a180285喜歡用最短的滑行路徑去訪問盡量多的景點。

假設只訪問一條路徑上的景點。他會覺得數量太少。於是a180285拿出了他隨身攜帶的時間膠囊。這是一種非常奇妙的藥物,吃下之後能夠馬上回到上個經過的景點(不用移動也不被覺得是a180285 滑行的距離)。

請註意,這樣的奇妙的藥物是能夠連續食用的,即能夠回到較長時間之前到過的景點(比方上上個經過的景點和上上上個經過的景點)。 如今,a180285站在1號景點望著山下的目標。心潮澎湃。他十分想知道在不考慮時間
膠囊消耗的情況下,以最短滑行距離滑到盡量多的景點的方案(即滿足經過景點數最大的前提下使得滑行總距離最小)。你能幫他求出最短距離和景點數嗎?

Input

輸入的第一行是兩個整數N。M。
接下來1行有N個整數Hi。分別表示每一個景點的高度。
接下來M行,表示各個景點之間軌道分布的情況。每行3個整數,Ui,Vi。Ki。表示
編號為Ui的景點和編號為Vi的景點之間有一條長度為Ki的軌道。

Output

輸出一行,表示a180285最多能到達多少個景點。以及此時最短的滑行距離總和。

Sample Input

3 3 
3 2 1 
1 2 1 
2 3 1 
1 3 10 

Sample Output

3 2 

HINT

【數據範圍】

對於30%的數據。保證 1<=N<=2000 

對於100%的數據,保證 1<=N<=100000 

對於全部的數據。保證 1<=M<=1000000,1<=Hi<=1000000000,1<=Ki<=1000000000。

題解:

第一問就是要找有多少點和1聯通,直接bfs一邊就好了。
第二問實際上是求一個最小樹形圖(就是有向圖上的最小生成樹)。
一般都是用朱劉算法的。然而感覺不大會寫並且復雜度有些不正確,於是想了想kruskal。發現我們只須要對全部第一問中找出的點做就好了,所以能夠對全部邊按終點的高度降序和邊長升序排序就好了。這樣就能夠克服kruskal可能會做出來不聯通的樹了。

Code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100100
#define M 2001000
#define LL long long

struct Edge{
    int u,v,next; LL k;
}edge[M<<1];
int n,m,num=0,ans1,head[N],h[N],fa[N],q[M<<1];
LL ans2; bool vis[N];

int in(){
    int x=0; char ch=getchar();
    while (ch<‘0‘ || ch>‘9‘) ch=getchar();
    while (ch>=‘0‘ && ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar();
    return x;
}
LL Lin(){
    LL x=0; char ch=getchar();
    while (ch<‘0‘ || ch>‘9‘) ch=getchar();
    while (ch>=‘0‘ && ch<=‘9‘) x=x*10+(LL)(ch-‘0‘),ch=getchar();
    return x;
}

void add(int u,int v,int k){
    edge[++num].u=u; edge[num].v=v; edge[num].k=k;
    edge[num].next=head[u]; head[u]=num;
}

void bfs(){
    int h=0,t=1; ans1=0;
    memset(vis,0,sizeof(vis));
    q[h]=1; vis[1]=1;
    while (h<t){
        int u=q[h++]; ans1++;
        for (int i=head[u]; i; i=edge[i].next){
            int v=edge[i].v;
            if (vis[v]) continue;
            q[t++]=v; vis[v]=1;
        }
    }
}

bool cmp(Edge x,Edge y){
    if (h[x.v]!=h[y.v]) return h[x.v]>h[y.v];
    return x.k<y.k;
}
int find(int x){
    if (x==fa[x]) return x;
    fa[x]=find(fa[x]);
    return fa[x];
}
void unionn(int x,int y){
    fa[x]=y;
}
void kruskal(){
    ans2=0;
    sort(edge+1,edge+num+1,cmp);
    for (int i=1; i<=n; i++) fa[i]=i;
    for (int i=1; i<=num; i++){
        int u=edge[i].u,v=edge[i].v;
        if (!vis[u] || !vis[v]) continue;
        int f1=find(u),f2=find(v);
        if (f1!=f2)
            unionn(f1,f2),ans2+=edge[i].k;
    }
}

int main(){
    n=in(),m=in();
    for (int i=1; i<=n; i++) h[i]=in();
    for (int i=1; i<=m; i++){
        int u=in(),v=in(); LL k=Lin();
        if (h[u]>=h[v]) add(u,v,k);
        if (h[v]>=h[u]) add(v,u,k);
    }

    bfs(); kruskal();

    printf("%d %lld\n",ans1,ans2);
    return 0;
}

【最小樹形圖(奇怪的kruskal)】【SCOI 2012】【bzoj 2753】滑雪與時間膠囊