Skip to content

P1955 [NOI2015] 程序自动分析

原题链接:P1955 [NOI2015] 程序自动分析

Tag:离散化、并查集

题目描述

在实现程序自动分析的过程中,常常需要判定一些约束条件是否能被同时满足。

考虑一个约束满足问题的简化版本:假设 \(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\), 这些约束条件显然是不可能同时被满足的,因此这个问题应判定为不可被满足。

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

数据说明:

\(1\leq n \leq 10^5,\ 1\leq i,j \leq 10^9,\ 1 \leq t \leq 10,\ e \in \{0,1\}\)

分析

首先考虑所有的 \(x = y\) 的情况,显然相等的变量之间存在传递的关系,即 \(a=b,b=c\) 同时成立时, 必然有 \(a = c\)

这说明题目中的限制本质上是把所有的点分成若干类, 也就是说只要最后某个不等式的 \(x \neq y\),其中 \(x,y\) 属于了同一组, 就会产生矛盾。

反过来说,如果我们事先按照所有的等式把所有变量进行分组, 在之后检查不等式的时候,如果没有发现任何一个不等式两边的变量落在同一组中, 那么就是符合题目要求的。

显然我们需要通过某种手段来维护这种分组的关系, 这一步可以通过并查集实现。

除此之外,注意到值的取值范围最大有 \(10^9\), 显然没有办法开 \(10^9\) 大小的并查集, 但是因为最多存在 \(n\) 个式子,也就是最多存在 \(2n\) 个本质不同的变量。 因此我们可以通过离散化来处理数字,对数字编号进行并查集上的维护即可。

代码实现

struct Discretize {
    vector<long long> vec;
    void add(long long x) {
        vec.push_back(x);
    }
    void init() {
        std::sort(vec.begin(), vec.end());
        vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
    }
    void init(vector<long long> v) {
        vec = std::move(v);
        std::sort(vec.begin(), vec.end());
        vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
    }
    int size() { return vec.size(); }
    int id(long long x) {
        return std::lower_bound(vec.begin(), vec.end(), x) - vec.begin() + 1;
    }
    //检查是否存在元素,存在返回下标,不存在返回 0
    int check_have(long long x) {
        auto it = std::lower_bound(vec.begin(), vec.end(), x);
        if (it == vec.end() || *it != x) return 0;
        return it - vec.begin() + 1;
    }
    long long val(int k) {
        return vec[k - 1];
    }
    // 查询有多少元素小于等于 x,x 可以不存在
    int rank_le(long long x) {
        return std::upper_bound(vec.begin(), vec.end(), x) - vec.begin();
    }
};

struct DSU {
public:
    vector<int> bcj;
    vector<int> siz;
    void Init(int n) {
        bcj.resize(n + 1);
        siz.resize(n + 1);
        for (int i = 1; i <= n; ++i) {
            bcj[i] = i;
            siz[i] = 1;
        }
    }
    int Find(int x) {
        if (bcj[x] == x) return x;
        return bcj[x] = Find(bcj[x]);
    }
    // 按秩合并
    void Union(int x, int y) {
        x = Find(x);
        y = Find(y);
        if (x == y) return;
        if (siz[x] > siz[y]) swap(x, y);
        bcj[x] = y;
        siz[y] += siz[x];
    }
};

void IHaveNoLimitation() {
    int n;
    cin >> n;
    struct node {
        int x, y, t;
    };
    vector<node> vec(n);
    Discretize dc;
    for (auto &[x, y, t]: vec) {
        cin >> x >> y >> t;
        dc.add(x), dc.add(y);
    }
    dc.init();
    std::sort(vec.begin(), vec.end(), [&](auto &x, auto &y) {
        return x.t > y.t;
    });
    DSU dsu;
    dsu.Init(dc.size() + 1);
    for (auto &[x, y, t]: vec) {
        if (t == 1) {
            dsu.Union(dc.id(x),dc.id(y));
        }else{
            if(dsu.Find(dc.id(x)) == dsu.Find(dc.id(y))){
                cout << "NO" << endl;
                return;
            }
        }
    }
    cout << "YES" << endl;
}

日志

本页面创建于 2026/3/1 11:08