[BZOJ2753] [SCOI2012]滑雪与时间胶囊

题目描述

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。

题目分析:

有向图求最小生成树应该用朱刘算法做 然而不仅不会而且TLE 所以我们还是要用最小生成树
对于第一问 一个BFS或者DFS就搞定了 关键是第二问 因为只有高度高的才能到低的 我们就从高到低连一条边
对于高度相同要连双向边 因为第一问搜索如果不连双向边找不到更优解 然后我们将边从到达点的高度降序排序 如果到达点的高度相同就按边权排序 最后跑最小生成树即可

#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <algorithm>
using namespace std;
int n,m;
long long h[110000];
struct your
{
	int x,y;
	long long c;
}a[4110000];
int tot,num;
long long sum;
int head[110000],to[4100000],next[4100000];
void add(int x,int y)
{
	next[++tot]=head[x];
	to[tot]=y;
	head[x]=tot;
}
bool vis[110000];
queue<int>q;
void bfs()
{
	q.push(1);
	vis[1]=true;
	while(q.size())
	{
		int nmp=q.front();
		q.pop();num++;
		for(int i=head[nmp];i;i=next[i])
			if(!vis[to[i]])
				vis[to[i]]=true,q.push(to[i]);
	}
}
bool cmp(your j,your k)
{
	if(h[j.y]==h[k.y]) return j.c<k.c;
	return h[j.y]>h[k.y];
}
int cnt;
int f[1100000];
int find(int x)
{
	return f[x]==x?x:f[x]=find(f[x]);
}
void kru()
{
	for(int i=1;i<=m;i++)
	{
		if(!vis[a[i].x]||!vis[a[i].y]) continue;
		int dx=find(a[i].x),dy=find(a[i].y);
		if(dx!=dy) f[dx]=dy,sum+=a[i].c,cnt++;
		if(cnt==n-1) return ;
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%lld",&h[i]),f[i]=i;
	for(int i=1;i<=m;i++)
	{
		int x,y;
		long long c;
		scanf("%d%d%lld",&x,&y,&c);
		if(h[x]>=h[y]) add(x,y),a[i].x=x,a[i].y=y,a[i].c=c;
		if(h[x]<=h[y]) add(y,x),a[i].x=y,a[i].y=x,a[i].c=c;
 	}
 	sort(a+1,a+m+1,cmp);
    bfs();kru();
    printf("%d %lld",num,sum);
    return 0;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注