The 15th Chinese Northeast Collegiate Programming Contest - K.City

16 篇文章 0 订阅
13 篇文章 0 订阅

在这里插入图片描述
在这里插入图片描述
虽然不难,但是感觉很有意思的一道题,下面有几个点感觉还是挺妙的:
· 并查集只能加边不能减边;
·对数据进行离线处理,方便我们进行排序处理(后面会解释);
首先数据的范围很大,如果我们每做一次判断都要进行一次遍历的话时间复杂度肯定会超时,所以我们想能不能像预处理那样只需要做一次就能得到所有我们想要的答案呢?
首先我们先对所有边进行一个从大到小的排序,所以我们在判断毁掉了多少条路的时候只需要判断当前边的值是否小于毁灭的值即可,如果不能毁掉那么后面的也无法破坏,break就好。
那么还是没有解决 O(t * m * q)这样一个时间复杂度,接下来我们对查询的值也进行一个从大到小的排序(注意我们输出的时候还是要按原顺序输出,这个地方定义一个id编号即可),排序之后刚开始我们从大到小进行找,因为查询值大的无法破坏的边,后面的小的查询值也无法破坏,所以我们换个思路,题目给的是破坏的多少条边,反过来经过排序之后逆向思维可以理解成查询的值变小了又加入了多少条边,这样我们就可以用并查集来进行处理了。
如果一个集合里面有n个元素,那么就有1+2+3+……+n-1个城市对。为了避免每次做完查询操作都要对集合进行一个遍历,所以我们用一个ans来记录目前为止符合条件的城市对的数量,遇到可以有新加入的点我们把对应的值加进去。但是两个集合合并的话不能进行一个简单的相加,因为会有重复,意思是我们之前这两个集合各自的城市对已经加入ans了,合并之后又加一遍会有数据重复,所以这里处理可以有两种做法,一种是先减去两个集合各自城市对的数量,然后合并之后再加上合并之后的城市对的数量,另一种做法是对前一种做法的一个整理,假设第一个集合里面有n个城市,第二个集合里面有m个城市,所以第一个集合的城市对有n*(n-1)/2,第二个集合的城市对有m*(m-1)/2,合并之后总的城市对有 (n+m) * (n+m-1)/2,整理后为 (n+m) * (n+m-1)/2 - n*(n-1)/2 - m*(m-1)/2 = n*m,所以我们合并两个集合的时候加n * m 也可以。
完整代码如下:

#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
const int maxn = 2*1e5+100;
 
struct infor{
	int x,y;
	int cost;
}e[maxn];
 
struct lian{
	long long sum;
	int f;
}fa[maxn];
 
struct qs{
	long long zhi;
	int id;
}se[maxn];
 
bool cmp1(infor a,infor b){
	return a.cost > b.cost;
}
bool cmpc(qs a,qs b){
	return a.zhi > b.zhi;
}
 
int find(int x){
    if(x==fa[x].f) return x;
    else fa[x].f = find(fa[x].f);
    return fa[x].f;
}
 
void solve(){
	
	long long jie[maxn];
	int n,m,q;
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=n;i++){
		fa[i].sum = 1;
		fa[i].f = i;
	}
	for(int i=0;i<m;i++){
		scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].cost);
	}
	for(int i=0;i<q;i++){
		scanf("%lld",&se[i].zhi);
		se[i].id = i;
	}
	sort(e,e+m,cmp1);
	sort(se,se+q,cmpc);
	int j=0;
	long long ans = 0;
	for(int i=0;i<q;i++){
		for(j;j<m;j++){
			int from = e[j].x;
			int to = e[j].y;
			int c = e[j].cost;
			if(c<se[i].zhi) break;
			else{
				from = find(from);
				to = find(to);
				if(from!=to){
					long long a,b;
					a = fa[from].sum;
					b = fa[to].sum;
					fa[from].f = to;
					fa[to].sum += fa[from].sum;
					ans += fa[to].sum*(fa[to].sum-1)/2 - a*(a-1)/2 - b*(b-1)/2;
				}
			}
		}
		jie[se[i].id] = ans;
	}
	for(int i=0;i<q;i++){
		printf("%lld\n",jie[i]);
	}
}
int main(){
	int t;
	cin >> t;
	while(t--){
		solve();
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值