[SCOI2012]滑雪与时间胶囊

题目:BZOJ2753、洛谷P2573、codevs2399。

题目大意:给你一张带权有向图(由于存在高度限制,所以边是有向的),问你从1开始最多能深度优先遍历多少个点(使用时间胶囊即为回溯),在遍历最多点的情况下,使得所有经过的边的权值和最小(由于深度优先遍历,所以就是求“有向图的最小生成树”)。

解题思路:第一问很容易,深搜遍历即可。

在遍历的时候把有用的边记录下来。

第二问其实就是求最小生成树,但边是有向的,怎么办?

这里仍然可以使用Kruskal解决。

我们可以在对边排序的时候,以终点的高度为第一关键字,从大到小排,以边权为第二关键字,从小到大排。

要保证有向图的“最小生成树”正确,我们必须从高到低按次序遍历下来。

而以终点的高度从大到小排,就能保证所遍历的点一定是从大到小的,也就保证了答案的正确性。

然后Kruskal跑即可。

时间复杂度$O(m\log_2 m)$,时间限制5s,强的数据能在1s左右运行完。

注意答案会超出$2^{32}$,要用64位整数。

C++ Code:

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,h[100002],cnt,head[100002],fa[100002],cnt2,mxJD,head2[100002];
long long mxDS;
bool vis[100002];
int dad(int x){return fa[x]==x?x:fa[x]=dad(fa[x]);}
struct edge{
	int from,to,dis,nxt;
	bool operator<(const edge& rhs)const{
		if(h[to]!=h[rhs.to])return h[to]>h[rhs.to];
		return dis<rhs.dis;
	}
}e[1000005<<1],e2[1000005<<1];
inline int readint(){
	char c=getchar();
	for(;!isdigit(c);c=getchar());
	int d=0;
	for(;isdigit(c);c=getchar())
	d=(d<<3)+(d<<1)+(c^'0');
	return d;
}
void dfs(int now){
	vis[now]=true;
	++mxJD;
	for(int i=head[now];i;i=e[i].nxt){
		e2[++cnt2]=(edge){e[i].from,e[i].to,e[i].dis,head2[e[i].from]};
		head2[e[i].from]=cnt2;
		if(!vis[e[i].to])dfs(e[i].to);
	}
}
int main(){
	memset(head,0,sizeof head);
	cnt=cnt2=mxJD=mxDS=0;
	n=readint(),m=readint();
	for(int i=1;i<=n;++i)h[i]=readint(),fa[i]=i;
	while(m--){
		int u=readint(),v=readint(),t=readint();
		if(h[u]>=h[v]){
			e[++cnt]=(edge){u,v,t,head[u]};
			head[u]=cnt;
		}//NOT else
		if(h[u]<=h[v]){
			e[++cnt]=(edge){v,u,t,head[v]};
			head[v]=cnt;
		}
	}
	memset(head2,0,sizeof head2);
	memset(vis,0,sizeof vis);
	dfs(1);
	printf("%d ",mxJD);
	sort(e2+1,e2+cnt2+1);
	for(int i=1;i<=cnt2;++i){
		int a=dad(e2[i].from),b=dad(e2[i].to);
		if(a!=b){
			mxDS+=e2[i].dis;
			fa[b]=a;
			if(--mxJD<2)break;
		}
	}
	printf("%lld\n",mxDS);
	return 0;
}

 

转载于:https://www.cnblogs.com/Mrsrz/p/7761522.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值