洛谷 P1955 [NOI2015] 程序自动分析【并查集】【离散化】

124 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

题目链接:洛谷 P1955

题目大意:对于每个提问,给出若干个变量约束条件(等于或不等于),问能否找到一组变量的值,使得满足给出的若干变量约束条件。

题目分析:

对于此题,我们不妨将每个变量 xix_i 视作一个点,如果存在相等约束关系:xi=xjx_i=x_j,那么显然这两个值就是相等的,因此我们可以考虑将这两个变量 xix_ixjx_j 放入同一个集合当中。如果约束关系为不相等,那么我们就只需要直接判断两个变量是否处于不同集合,如果已经处于了相同集合,说明情况矛盾。

按照这个思路开展下来,一直到最后,如果也没有出现矛盾的情况,那就说明我们可以得到一组满足条件的值。

这样的一个集合关系我们可以使用并查集来处理,使用并查集的基本「合并」与「查询」操作就可以实现元素与集合的处理。

要注意的是,原始数据范围 10910^9 太大,我们无法直接建立一个长度 10910^9 的数组来进行维护,这个时候就需要用到离散化了。

既然选择用离散化,那么本题自然而然就需要离线处理,我们用 a[i],b[i],c[i]a[i],b[i],c[i] 来存储输入当中的 i,j,ei,j,e,然后我们用另外一个数组 map[]map[] 来接收离散化后的结果,对于离散化后的数据,我们就可以将 mapmap 排序后,用二分查找进行查找原来的那个数即可。

参考代码:

#include <cstdio>
#include <algorithm>
using namespace std;
​
const int N = 1e6+5;
​
int n, t, o;
int a[N], b[N], c[N];
int f[N], map[N<<1];
​
int find(int x) {
    return f[x] == x ? x : f[x] = find(f[x]);
}
​
int query(int x) {
    return lower_bound(map + 1, map + 1 + (n << 1), x) - map;
}
​
void merge(int x, int y) {
    int fx = find(x), fy = find(y);
    if (fx != fy) {
        f[fx] = fy;
    }
}
​
int main() {
    scanf("%d", &t);
    while(t--) {
        o = 0;
        scanf("%d", &n);
        for (int i = 1; i <= n; ++i) {
            scanf("%d%d%d", &a[i], &b[i], &c[i]);
            map[(i << 1) - 1] = a[i];
            map[i << 1] = b[i];
        }
        sort(map + 1, map + 1 + (n << 1));
        for (int i = 1; i <= n << 1; ++i) {
            f[i] = i;
        }
        for (int i = 1; i <= n; ++i) {
            if (c[i] == 1) {
                int nx = query(a[i]), ny = query(b[i]);
                merge(nx, ny);
            }
        }
        for (int i = 1; i <= n; ++i) {
            if (c[i] == 0) {
                int nx = query(a[i]), ny = query(b[i]);
                if(find(nx) == find(ny)) {
                    o = 1;
                    break;
                }
            }
        }
        if (o) {
            puts("NO");
        } else {
            puts("YES");
        }
    }
    return 0;
}