NOI2015 程序自动分析 并查集 + 离散化

原题链接

洛谷

题目描述

思路

对于“相等”的约束条件,易得,可将每个 x 看作一个点,每个“相等”的条件看作两个点的一条边,然后将所有 x 就分成无向图中的若干个集合。

用并查集维护这些集合。先处理所有“相等”的条件,合并每两个 x 的集合。然后遍历“不等”的条件,如果存在两个x处于同一集合,那么就不可被满足,输出“NO”;若不存在,则可以被满足,输出“YES”。

但是,本题 x 的范围是 1 <= x <= 1e9,需要用到离散化的技巧。把变量 x 的范围映射到1 ~ 2n之内,再用上述算法解决。

离散化

就是把无穷大集合中的若干个元素映射为有限集合。

有时候,数据范围虽然定义在整数集合 Z,但是只涉及其中 m 个有限数值,并且与数值的绝对大小无关。离散化就是把整数集合 Z 中的这 m 个数与1 ~ m建立映射关系。

a 原数组

b 离散化后的数组

sort(a + 1, a + n + 1); // 排序
int m = unique(a + 1, a + n + 1) - a; // 去重

查询 x 离散化后到 1 ~ m 的整数
lower_bound(a + 1, a + m + 1, x) - a;  
        

并查集

初始化
for (int i = 1; i <= n; i ++ )
    p[i] = i; 
​
查集
int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}
 
并集
p[find(x)] = find(y);

完整Code

#include <bits/stdc++.h>
using namespace std;

const int N = 3e5 + 10;

int p[N];
int n, b[N];
struct Node {
	int x1, x2, e;
} a[N];  

int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    int T; cin >> T;
    while (T -- )
    {
        // 别忘了初始化
        memset(b, 0, sizeof b);
        memset(p, 0, sizeof p);
        memset(a, 0, sizeof a);
        
        cin >> n;
        for (int i = 1; i <= n; i ++ )
		{
            cin >> a[i].x1 >> a[i].x2 >> a[i].e;
            b[i * 2 - 1] = a[i].x1; 
			b[i * 2] = a[i].x2;
        }
        
        // 离散化
        sort(b + 1, b + n * 2 + 1);
        int m = unique(b + 1, b + 2 * n + 1) - b; 

        for (int i = 1; i <= n; i ++ )
		{ 
           a[i].x1 = lower_bound(b + 1, b + m + 1, a[i].x1) - b;
           a[i].x2 = lower_bound(b + 1, b + m + 1, a[i].x2) - b;   
        } 
        	
        for (int i = 1; i <= m; i ++ ) 
			p[i] = i;
		
        // 并集    
        for (int i = 1; i <= n; i ++ )
			if (a[i].e) p[find(a[i].x1)] = find(a[i].x2);
        
        // 检验是否可以被满足
        bool f = true;    	
        for (int i = 1; i <= n; i ++ )
			if (!a[i].e && find(a[i].x1) == find(a[i].x2))
			{
                cout << "NO" << endl;
                f = false; break ;
            }
        
        if (f) cout << "YES" << endl;
	}
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值