[SCOI2012]滑雪与时间胶囊
题目描述:
a180285非常喜欢滑雪。他来到一座雪山,这里分布着M条供滑行的轨道和N个轨道之间的交点(同时也是景点),而且每个景点都有一编号i(1 ≤ i ≤ N)和一高度Hi。a180285 能从景点i 滑到景点j 当且仅当存在一条i 和j 之间的边,且i 的高度不小于j。
与其他滑雪爱好者不同,a180285喜欢用最短的滑行路径去访问尽量多的景点。如果仅仅访问一条路径上的景点,他会觉得数量太少。于是a180285拿出了他随身携带的时间胶囊。
这是一种很神奇的药物,吃下之后可以立即回到上个经过的景点(不用移动也不被认为是 a180285 滑行的距离)。
请注意,这种神奇的药物是可以连续食用的,即能够回到较长时间 之前到过的景点(比如上上个经过的景点和上上上个经过的景点)。
现在,a180285站在1号景点望着山下的目标,心潮澎湃。他十分想知道在不考虑时间 胶囊消耗的情况下,以最短滑行距离滑到尽量多的景点的方案(即满足经过景点数最大的前 提下使得滑行总距离最小)。你能帮他求出最短距离和景点数吗?
输入描述:
输入的第一行是两个整数N,M。
接下来1行有N个整数Hi,分别表示每个景点的高度。
接下来M行,表示各个景点之间轨道分布的情况。每行3个整数,Ui,Vi,Ki。表示编号为Ui的景点和编号为Vi的景点之间有一条长度为Ki的轨道。
输出描述:
输出一行,表示a180285最多能到达多少个景点,以及此时最短的滑行距离总和。
数据范围:
对于30%的数据,保证1<=N<=2000
对于100%的数据,保证1<=N<=100000
对于所有的数据,保证1<=M<=1000000, 1<-Hi<=100000000, 1<=Ki<= 100000000。
思路: 首先根据题意,我们要根据每个点的高度 H 建一个有向图(并且用结构体储存这些边),那么从点 1 开始bfs / dfs 就可以得到从点1 能到达的所有点的个数。那么一定存在一个树形图连接起这些点,但是如何使这个树形图为最小树形图(据说有个"朱-刘Edmonds“算法,但好像是O(nv)的复杂度,而且我不会),其实就是在有向图里求最小生成树。
回到问题上,普通的
k
r
u
s
k
a
l
kruskal
kruskal 会把所有的边都当为无向边来求解,但对于此问题只需要一些变形就可以达到有向边的效果。首先我们结构体储存的边都是满足条件的边,即
h
[
a
]
>
=
h
[
b
]
h[a]>=h[b]
h[a]>=h[b] ,所以我们可以以
h
[
b
]
h[b]
h[b]为第一关键字从大到小排序,以边权
k
k
k为 第二关键字从小到大排序,这样当我们跑
k
r
u
s
k
a
l
kruskal
kruskal时,先拓展的都是第一层最高的点,那么到拓展第二层的点时,第一层的都已经加入到了图中,而与第二层相连的点都是在二层或一层的,所以这样就能满足拓展时点的高度非递减;
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6+7;
int H[N],p[N];
bool vis[N];
int n,m;
LL ans1,ans2;
struct Node{
int a,b,k;
}E[N*2];
int h[N],e[N*2],ne[N*2],idx;
void add(int a,int b){
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
int find(int x){
if(p[x]!=x) p[x] = find(p[x]);
return p[x];
}
bool cmp(Node a,Node b){
if(H[a.b]!=H[b.b]) return H[a.b] > H[b.b];
return a.k < b.k;
}
void dfs(int x)
{
ans1++,vis[x] = 1;
for(int i=h[x];~i;i=ne[i]){
int j = e[i];
if(vis[j]) continue;
dfs(j);
}
}
void kruskal()
{
sort(E,E+m,cmp);
for(int i=1;i<=n;i++) p[i] = i;
for(int i=0;i<m;i++){
if(!vis[E[i].a]||!vis[E[i].b]) continue;
int x = find(E[i].a) , y = find(E[i].b);
if(x!=y){
p[x] = y;
ans2+=(LL)E[i].k;
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>H[i];
memset(h,-1,sizeof h);
for(int i=0;i<m;i++){
int a,b,c;
cin>>a>>b>>c;
if(H[a]>=H[b]) add(a,b),E[i] = {a,b,c};
if(H[b]>=H[a]) add(b,a),E[i] = {b,a,c};
}
dfs(1);
kruskal();
cout<<ans1<<" "<<ans2<<"\n";
return 0;
}