【最小樹形圖(奇怪的kruskal)】【SCOI 2012】【bzoj 2753】滑雪與時間膠囊
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】滑雪與時間膠囊