P3101 [USACO14JAN]Ski Course Rating G-题解报告

0x00 前言

此题需要使用并查集来解决,题目标签的二分是假的吧…

0x01 思路

  • 建边

此题的思路比较简单,首先建边,一个点的第一条边是它的右边那个点,第二条边是下面那个点,边的权值为两个点差的绝对值。

  • 加边

首先需要对边进行一个排序,按照边的权值从小到大排序,因为我们需要让答案尽可能的小,然后依次枚举每一条边,如果这条边的两个点已经在一个集合内了,就不需要去管。如果两个点没有在一个集合内,就需要去连接,当两个集合的数量加起来大于了 t t t 那么就可以加答案,并且将这条边连接起来。

大致思路还是很简单,但是有十分多的细节。

0x02 技巧

  • 数组范围

我们观察数据

n , m ≤ 500 n,m\le500 n,m500

因为我们需要用并查集来统计,所以可以把这个二维数据压缩成一维,那么点的个数就是 n × m n \times m n×m,因为每个点可以连接两条边,所以边的数组就需要开点的个数的两倍。

  • 如何建边

我们可以用一个数组来标记当前点是第几个点,用一个变量去累加当前是第几个点就可以了,由于后面加边的需要,我们要开一个用来统计当前这个集合要作为起点的个数。然后求权值就很简单了。

  • 如何加边

加边要注意的就是当两个集合的点的数量加起来大于等于 t t t 时,需要逐次判断两个区间,为什么呢?因为如果其中一个区间点的个数已经达到了 t t t,前面已经加过了,就不用再加了,然后合并的时候就按照并查集的基本操作合并就好。

0x03 代码

#include<bits/stdc++.h>
#define int long long
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define dep(i,r,l) for(int i=r;i>=l;i--)
using namespace std;
int n,m,t,tot,to,ans;
int fa[2500005];
int s[2500005];
int ma[505][505];
int bj[2500005];
int bh[505][505];
struct node{
	int x,y,f;
}a[2500005];
bool cmp(node l,node r){return l.f<r.f;}
int find(int x){
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
signed main(){
	scanf("%lld %lld %lld",&n,&m,&t);
	rep(i,1,n)
		rep(j,1,m){
			scanf("%lld",&ma[i][j]);
			tot++;
			bh[i][j]=tot;
		}
	rep(i,1,n){
		rep(j,1,m){
			int bjj;
			scanf("%lld",&bjj);
			if(bjj) bj[(i-1)*m+j]=1;
			if(j!=m){
				a[++to].f=abs(ma[i][j+1]-ma[i][j]);
				a[to].x=bh[i][j];
				a[to].y=bh[i][j+1];
			}
			if(i!=n){
				a[++to].f=abs(ma[i+1][j]-ma[i][j]);
				a[to].x=bh[i][j];
				a[to].y=bh[i+1][j];
			}		
		}
	}
	sort(a+1,a+1+to,cmp);
	rep(i,1,tot*2){
		fa[i]=i;
		s[i]=1;
	}
	rep(i,1,to){
		int x=find(a[i].x),y=find(a[i].y);
		if(x==y) continue;
		if(s[x]+s[y]>=t){
			if(s[x]<t) ans+=a[i].f*bj[x];
			if(s[y]<t) ans+=a[i].f*bj[y];
		}
		bj[x]+=bj[y];
		s[x]+=s[y];
		fa[y]=x;
	}
	printf("%lld",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值