题解:AT_abc040_d [ABC040D] 道路の老朽化対策について

一道很经典的并查集离线题。

Solution

Idea

我们发现每一次都对于一个询问单独加边,时间复杂度肯定会爆炸。

于是我们思考:如何才能让加边的次数变少呢?

不难发现,我们的每一条边都有一个修建年份,并且修建在 xx 年的一定会被排斥 yy 年以前的使用,当且仅当 y<xy<x 时。

如果我们对于询问按照排斥年份从晚到早排序,把边按照修建时间从晚到早排序,则我们先加入的边一定不会被后面的询问排斥,因为显然越靠后的人所能接受的年份越早。

显然有一个要求:对于每一组询问只能添加修建年份更晚的边。如果我们按照排序,每一次更新,我们都只需在原有的基础上添加修建年份在 wi1w_{i-1}wiw_i 之间的边,就可以做到在不多不少的情况下恰好加 nn 次边。

注意因为排了序,所以要记录询问序号,然后最后统一输出。

Code

#include<bits/stdc++.h>
using namespace std;
const int N=100005,M=200005;
int n,m;
struct node{
	int u,v,y;
	friend bool operator <(node _,node __){
		return _.y>__.y;
	}
}a[M];
struct ask{
	int x,y,id;
	friend bool operator <(ask _,ask __){
		return _.y>__.y;
	}
}e[N];
int fa[N],siz[N];
int findd(int now){
	if(fa[now]==now)return now;
	return fa[now]=findd(fa[now]);
}
void vnion(int u,int v){
	u=findd(u),v=findd(v);
	if(u==v)return;
	fa[u]=v;
	siz[v]+=siz[u];
}
int q,ans[N];
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		fa[i]=i;
		siz[i]=1;
	}
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].y);
	}
	sort(a+1,a+m+1);
	scanf("%d",&q);
	for(int i=1;i<=q;i++){
		scanf("%d%d",&e[i].x,&e[i].y);
		e[i].id=i;
	}
	sort(e+1,e+q+1);
	for(int i=1,j=1;i<=q;i++){
		while(j<=m&&a[j].y>e[i].y){
			vnion(a[j].u,a[j].v);
			j++;
		}
		ans[e[i].id]=siz[findd(e[i].x)];
	}
	for(int i=1;i<=q;i++)printf("%d\n",ans[i]);
	return 0;
}
posted @   Weslie_qwq  阅读(5)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示