P1955 [NOI2015] 程序自动分析 题解

题目传送门

我的博客

前言

前几天写的一道题,做法是离散化+并查集,今天补一下题解。

思路

首先看到这种二元关系题,会想到并查集、二分图。在看这道题貌似和图论没关系,所以大体锁定要用并查集。

当我们遇到 \(x_i = x_j\) 时,肯定要让 \(i,j\) 加入一个并查集中。这种情况直接无脑加即可。为什么这里不需要判断呢?因为现在没法判断 \(i,j\) 不在一个并查集中是因为 \(x_i \neq x_j\) 还是因为 \(i,j\) 之间目前还没有限制条件。

\(x_i \neq x_j\) 时,就需要判断当前是否合法了。如果 \(i,j\) 已经在一个并查集中,此时一定不合法。否则继续判断。

于是我们可以离线处理。先把所有 \(x_i = x_j\) 的加入到一个并查集里,再用 \(x_i \neq x_j\) 进行合法性判断。

再看数据范围 \(1 \leq i,j \leq 10^9\),但是 \(1 \leq n \leq 10^5\),那么我们很自然的想到离散化。(不会离散化的戳这里

代码

const int N=2e5+10;
struct nodein{
	int i,j,e;
}a[N];//输入
bool cmp(nodein A,nodein B){return A.e>B.e;}//先把所有 = 的并到一个集里
int T,n,ain[N<<1],tot;//ain:离散化数组
int fa[N<<1];
bool fl=0;
void inIt(){//多测清空
	tot=0;fl=1;
	for(int i=1;i<=2*n;i++) fa[i]=i;
	for(int i=1;i<=2*n;i++) ain[i]=0;
}
//并查集板子
int findf(int x){
	if(fa[x]==x) return x;
	return fa[x]=findf(fa[x]);
}
void merge(int x,int y){
	int fx=findf(x),fy=findf(y);
	if(fx!=fy){
		fa[fx]=fy;
		return ;
	}
	return ;
}
//判断是否合法
bool check(int x,int y){
	int fx=findf(x),fy=findf(y);
	if(fx==fy) return 0;
	return 1;
}
void solve(){
	n=Read();
	inIt();
	for(int i=1;i<=n;i++){
		int x=Read(),y=Read(),e=Read();
		a[i]=(nodein){x,y,e};//离线处理
		ain[++tot]=x;ain[++tot]=y;
	}
    //离散化板子
	sort(ain+1,ain+tot+1);
	int cnt=unique(ain+1,ain+tot+1)-(ain+1);//去重
	for(int i=1;i<=n;i++){
		a[i].i=lower_bound(ain+1,ain+cnt+1,a[i].i)-ain;
		a[i].j=lower_bound(ain+1,ain+cnt+1,a[i].j)-ain;
	}
	sort(a+1,a+n+1,cmp);//按照先 = 后 != 的顺序处理
	for(int i=1;i<=n;i++){
		if(a[i].e==1){
			merge(a[i].i,a[i].j);
		}
		if(a[i].e==0){
			if(!check(a[i].i,a[i].j)) fl=0;
		}
		if(!fl) break;
	}
	if(fl) puts("YES");
	else puts("NO");
}
signed main(){
	T=Read();
	while(T--){
		solve();//多测函数好!
	}
	return 0;
}
posted on 2025-11-06 11:28  _Liuliuliuliuliu  阅读(7)  评论(0)    收藏  举报