NOI2015 day1 prog 程序自动分析(并查集)

程序自动分析
【问题描述】
在实现 程序自动分析 的过程 中,常 需要判定一些 约束 条件是否能被 同时 满 足。
考虑 一个约束满足问题 的简化版本: 假设 x1,x2,x3,⋯ 代表 程序中出现的变 量,给定 n 个形如 xi=xj或 xi≠xj 的变量相等 变量相等 变量相等 变量相等 /不等 的约束 条件 ,请判定 是否 可以 分别 为每一个变量 赋予恰当的 值, 使得上述所有 约束 条件同时被 满足 。例如 , 一个问题 一个问题 中的约束 条件 为:x1=x2,x2=x3,x3=x4,x1≠x4 ,这些约束条件 ,这些约束条件 ,这些约束条件 显 然是不可 能同时被 满足的 ,因此 这个问题 应判定为不可被满足 。

现在给出一些约束满足 问题,请分别 对它们进行 判定。

【输入格式】
输入文件的第 1 行包含 1 个正整数 t ,表示 ,表示 需要判定的问题个数 。注意这些 注意这些 注意这些 问题 之间 是相互独立的。 是相互独立的。 是相互独立的。
对于每个 问题 ,包含 若干行 :
第 1 行包含 1 个正整数n ,表示 该问题中需要被满足的 约束 条件 个数 。
接下来 n行,每包括 行,每包括 3 个整数 i,j,e ,描述 1 个相等 /不等的 约束 条件 ,相 邻整数之间用单个 整数之间用单个 整数之间用单个 整数之间用单个 空格隔开。 空格隔开。 空格隔开。 若 e=1 ,则 该约束条件为 该约束条件为 该约束条件为 该约束条件为 xi= xj ;若 e=0 , 则该约束条件为 xi ≠ xj

【输出格式】
输出 文件包括 t 行。
输出 文件的 第 k行输出 一个字符串“ 一个字符串“ YES ”或者 “NO ”(不包含引号,字母 (不包含引号,字母 (不包含引号,字母 (不包含引号,字母 (不包含引号,字母 (不包含引号,字母 (不包含引号,字母 全部大写) 全部大写) 全部大写) ,“YES ”表示 输入中的第 k 个问题 判定为 可以 被满足, “NO ”表示不 可被满足。



哦很久没写blog了。。本沙茶这次NOI网赛唯一全A的一题 T_T 。。

这题一眼并查集,因为等号具有传递性,将所有用等号连接的集合合并一下就行了。但是要注意只能开一个并查集来维护所有等量关系,千万不要在开一个维护不等关系!(a!=b, b!=c, 合并后会推出a!=c,然而并不一定这样)先将所有等量关系维护后,再弄依次判断不等关系是否和集合冲突即可。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 1000010;
int N, T;
int fa[MAXN*2];
void get(int &r) { //n很大,数据组数也多,最好加读入外挂
    char c; r=0;
    do c=getchar(); while (c<'0'||c>'9');
    do{r=r*10+c-'0';c=getchar();}while (c>='0'&&c<='9');
}

int data[MAXN][2]; //记录输入数据以离散化
int b[MAXN*2]; //记录是等号还是不等号
int arr[MAXN * 2], cnt, len;

int bifind(int x) {  //二分查找(lower_bound比手写的慢三倍。。)
	int l = 1, r = len, mid = (l + r) >> 1;
	while (l < r) {
		if (arr[mid] == x) return mid;
		if (arr[mid] < x) l = mid + 1;
		else r = mid - 1;
		mid = (l + r) >> 1;
	}
	return mid;
}

struct Stack { //无聊的手写栈
	int sta[MAXN*2], tp;
	bool empty() {return tp==0;}
	int top() {return sta[tp];}
	void push(int x) {sta[++tp]=x;}
	void pop() {--tp;}
	void clear() {tp=0;}
} z;

void init() {
	for (int i = 1; i<=len; ++i) //这里是len不是n。。n是关系的个数,len才是变量个数
		fa[i] = i;
}
int root(int x) { //用手写栈实现的路径压缩防止递归爆栈。然而由于路径压缩的存在并不需要。。
	while (fa[x] != x && x != -1) z.push(x), x = fa[x];
	while (!z.empty()) fa[z.top()] = x, z.pop();
	return x;
}

bool solve() {
	init();
	int i, t1, t2;
	for (i = 1; i<=N; ++i) //先将所有相等关系合并成集合
		if (b[i] == 1) {
			t1 = bifind(data[i][0]);
			t2 = bifind(data[i][1]);
			if (root(t1) != root(t2))
				fa[root(t1)] = root(t2);
		}
	for (i = 1; i<=N; ++i)
		if (b[i] == 0) {
			t1 = bifind(data[i][0]);
			t2 = bifind(data[i][1]);
			if (root(t1) == root(t2)) //检查是否冲突
				return 0;
		}
	return 1;
}

int main()
{
	//freopen("prog.in", "r", stdin);
	//freopen("prog.out", "w", stdout);
	int i;
	get(T);
	while (T--) {
		get(N);
		cnt = 0;
		for (i = 1; i<=N; ++i) {
			get(data[i][0]); get(data[i][1]);
			get(b[i]);
			arr[++cnt] = data[i][0];
			arr[++cnt] = data[i][1];
		}
		sort(arr+1, arr+cnt+1);
		len = unique(arr+1, arr+cnt+1) - (arr+1); //len保存离散化后变量个数(重复出现不计)
		if (solve()) puts("YES");
		else puts("NO");
	}
	return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值