Loading

HDU - 6109 数据分割 并查集启发式合并

HDU - 6109 数据分割 并查集启发式合并

题意

一个程序会接受如\(x_i = x_j\)或者\(x_i \neq x_j\)的条件表示式。

现给出\(L\)行,碰到某一行发现矛盾则输出这一串表达式的个数,开启新的表达式。

e = 1 表示相等,0表示不相等

\[i,j,L \leq 100000\\ x_i , x_j \leq L \]

分析

联系之前洛谷做过的一道题,很容易想到这题用并查集来做。

我们可以把相等的点合并到一个并查集。

若遇到不等于,如果两点在同一个并查集,那么输出错误。如果不在同一个并查集,为了标记,在两个并查集的根之间连一条边,表示一种关系。

若遇到相等,如果两个点不在一个并查集,且已经有连边,则输出错误。

否则我们把两个并查集合并,注意这时候就要把边数较小的集合合并到另一个集合,这就是所谓的启发式合并。

这样每个点合并一次规模至少扩大两倍,复杂度就会是\(NlogN\)的。

不妨用\(set\)来支持快速删边加边操作。

代码

int pre[maxn];
set<int> e[maxn];
vector<int> ans;

int Find(int x) {
    return pre[x] != x ? pre[x] = Find(pre[x]) : x;
}

bool Union(int p, int q) {
    if (p != q) {
        if (e[p].size() > e[q].size())
            swap(p, q);
        for (auto it : e[p]) {
            if (it == q) return false;
            e[it].erase(p);
            e[it].insert(q);
            e[q].insert(it);
        }
    }
    pre[p] = q;
    return true;
}


int main() {
    int L = readint();
    int x, y, ee;
    int Max = -1;
    int siz = 0;
    for (int i = 1; i <= 100000; i++)
        pre[i] = i;
    for (int i = 1; i <= L; i++) {
        x = readint();
        y = readint();
        ee = readint();
        Max = max(Max, x);
        Max = max(Max, y);
        int p = Find(x);
        int q = Find(y);
        if (!ee) {
            if (p == q) {
                ans.push_back(i - siz);
                siz = i;
                for (int i = 1; i <= Max; i++)
                    pre[i] = i, e[i].clear();
                Max = -1;
                continue;
            }
            e[p].insert(q);
            e[q].insert(p);
        }
        else if (!Union(p, q)) {
            ans.push_back(i - siz);
            siz = i;
            for (int i = 1; i <= Max; i++)
                pre[i] = i, e[i].clear();
            Max = -1;
            continue;
        }
    }
    cout << ans.size() << '\n';
    for (int i = 0; i < ans.size(); i++)
        cout << ans[i] << '\n';
}
posted @ 2020-09-30 21:55  MQFLLY  阅读(141)  评论(0编辑  收藏  举报