Luogu P1955
在实现程序自动分析的过程中,常常需要判定一些约束条件是否能被同时满足。
考虑一个约束满足问题的简化版本:假设 \(x_1,x_2,x_3,\cdots\) 代表程序中出现的变量,给定 \(n\) 个形如 \(x_i=x_j\) 或 \(x_i\neq x_j\) 的变量相等/不等的约束条件,请判定是否可以分别为每一个变量赋予恰当的值,使得上述所有约束条件同时被满足。例如,一个问题中的约束条件为:\(x_1=x_2,x_2=x_3,x_3=x_4,x_4\neq x_1\),这些约束条件显然是不可能同时被满足的,因此这个问题应判定为不可被满足。
现在给出一些约束满足问题,请分别对它们进行判定。
输入的第一行包含一个正整数 \(t\),表示需要判定的问题个数。注意这些问题之间是相互独立的。
对于每个问题,包含若干行:
第一行包含一个正整数 \(n\),表示该问题中需要被满足的约束条件个数。接下来 \(n\) 行,每行包括三个整数 \(i,j,e\),描述一个相等/不等的约束条件,相邻整数之间用单个空格隔开。若 \(e=1\),则该约束条件为 \(x_i=x_j\)。若\(e=0\),则该约束条件为 \(x_i\neq x_j\)。
输出包括 \(t\) 行。
输出文件的第 \(k\) 行输出一个字符串 YES
或者 NO
(字母全部大写),YES
表示输入中的第 \(k\) 个问题判定为可以被满足,NO
表示不可被满足。
2 2 1 2 1 1 2 0 2 1 2 1 2 1 1
NO YES
2 3 1 2 1 2 3 1 3 1 1 4 1 2 1 2 3 1 3 4 1 1 4 0
YES NO
【样例解释1】
在第一个问题中,约束条件为:\(x_1=x_2,x_1\neq x_2\)。这两个约束条件互相矛盾,因此不可被同时满足。
在第二个问题中,约束条件为:\(x_1=x_2,x_1 = x_2\)。这两个约束条件是等价的,可以被同时满足。
【样例说明2】
在第一个问题中,约束条件有三个:\(x_1=x_2,x_2= x_3,x_3=x_1\)。只需赋值使得 \(x_1=x_2=x_3\),即可同时满足所有的约束条件。
在第二个问题中,约束条件有四个:\(x_1=x_2,x_2= x_3,x_3=x_4,x_4\neq x_1\)。由前三个约束条件可以推出 \(x_1=x_2=x_3=x_4\),然而最后一个约束条件却要求 \(x_1\neq x_4\),因此不可被满足。
【数据范围】
注:实际上 \(n\le 10^6\) 。
看到题目,可以将等于的条件看作边,那么所有的等于条件就是将一些点连在一起形成许多连通块。并且可以知道在等于条件连边的情况下是不可能出现与之前条件矛盾的情况,那么就可以将所有的等于条件提前至所有不等于条件之前来进行处理。
现在来看不等号的条件怎么处理,首先可以得出不等号的条件不会影响之后的任何不等号。因此将不等号当作不对条件产生影响的询问进行处理。因为我们将等于条件提前了,并且利用这些条件建了一个不一定全部联通的图,那么不等于条件就可以转化为询问不等号两边的数在图上是否是联通的。这样就将原题转化为了一个给出图判定连通性的问题。对于判定图的连通性的问题直接用带路径压缩的并查集就可以了。
另外需要注意到题目中 \(1\le i,j \le 1\times 10^9\) 这一条件,因为 \(1e9\) 的数据量没法直接用数组存下,而注意到 \(n\) 的范围是 \(1e5\) ,所以可以采用离散化对原数列进行处理,这样就将值域控制在了 \(1e5\) 这一数量级。
这样这道 \(\text{NOI}\) 的题就被我们切掉了。
#include<bits/stdc++.h> #define mem(a,b) memset(a,b,sizeof a) using namespace std; template<typename T> void read(T &k) { k=0;T flag=1;char b=getchar(); while (!isdigit(b)) {flag=(b=='-')?-1:1;b=getchar();} while (isdigit(b)) {k=k*10+b-48;b=getchar();} k*=flag; } const int _SIZE=1e6; int T,n; int fa[_SIZE+5],order[(_SIZE<<1)+5]; struct EQUAL{ int x,y,e; bool operator<(const EQUAL &a)const{ return e>a.e; } }a[_SIZE+5]; void init() { mem(fa,0);mem(order,0); } int find(int x) { if (x!=fa[x]) return fa[x]=find(fa[x]); return x; } int main() { read(T); while (T--) { read(n); for (int i=1;i<=n;i++) { read(a[i].x);read(a[i].y); read(a[i].e); order[(i<<1)-1]=a[i].x,order[(i<<1)]=a[i].y; } sort(order+1,order+(n<<1)+1); int tot=unique(order+1,order+(n<<1)+1)-order-1; for (int i=1;i<=n;i++) { a[i].x=lower_bound(order+1,order+tot+1,a[i].x)-order; a[i].y=lower_bound(order+1,order+tot+1,a[i].y)-order; //printf("%d %d\n",a[i].x,a[i].y); } sort(a+1,a+n+1); for (int i=1;i<=tot;i++) fa[i]=i; bool flag=0; for (int i=1;i<=n;i++) { int f1=find(a[i].x); int f2=find(a[i].y); if (a[i].e) fa[f1]=f2; else if (f1==f2) {flag=1;break;} //printf("%d %d\n",f1,f2); } if (flag) printf("NO\n"); else printf("YES\n"); } return 0; }