AcWing算法提高课 并查集

并查集基本操作:

1、合并两个集合

2、查询集合的代表元素(祖先节点)

 

优化方式:

1、路径压缩(合并查询复杂度都是logn)

2、按秩合并(每次合并将深度较小的合并到深度较大的,logn)

3、1+2可以达到O(α(n))≈O(1),α():反Ackermann函数

 

扩展:

1、记录集合大小

2、记录到根节点的距离

  通过到根节点的距离,表示与根节点的关系,从而达到对集合中的点进行分类

  例题:食物链

3、拓展域并查集

  拓展域中的点表示的不是变量而是变量的一个状态(取值)

 

模板:

基本并查集:

复制代码
int p[200010];

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

int merge(int x,int y)
{
    int px=find(x);
    int py=find(y);
    p[px]=p[y];
}
View Code
复制代码

 维护集合中全部物体的,价值\体积\数值的和,的并查集(和维护大小同理,绑定到根节点)

复制代码
int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}
void merge(int x, int y)
{
    int px = find(x);
    int py = find(y);
    if (px == py) return;
    val[py] += val[px];
    v[py] += v[px];
    p[px] = py;
}
View Code
复制代码

 维护距离的并查集(通过d和sz数组记录距离和集合大小,此模板合并时接到尾部,可以直接设置距离为sz的对应值)

(同时注意在find时要先存储原始的p[a],d[x]记录的是x到p[x]的距离,到祖先的距离会在find中更新):

复制代码
int p[30010];
int d[30010];
int sz[30010];
int find(int a)
{
    if (p[a] != a)
    {
        int opa = p[a];//注意下一句会更新p[a],所以要先把原始的p[a]存起来
        p[a] = find(p[a]);
        d[a] = d[opa] + d[a];//在find时会查找祖先,所以可以更新到祖先距离
    }
    return p[a];
}
void merge(int a, int b)
{
    int pa = find(a), pb = find(b);
    if (pa == pb) return;
    p[pa] = pb;//把a的祖先合并到b的祖先
    d[pa] = sz[pb];//距离设置为b的祖先的集合的大小,即把pa接到b所在集合的后面
    sz[pb] += sz[pa];//更新集合大小
}
View Code
复制代码

 

 

 

例题:

1、AcWing 1250. 格子游戏:

(并查集对一维坐标比较友好,可以把二维坐标转化为一维,一般从0开始的坐标比较方便)

2、求连通块可以用并查集

3、237. 程序自动分析

此题的数据范围较大但数据量1e5,故需要使用哈希表或离散化方式。

由于数据量问题,哈希表运行比较慢可能超时,所以需要离散化

离散化的一种(保序的)方式是,放入数组,排序,判重,查找时二分。

哈希表代码:

复制代码
#include <iostream>
using namespace std;
typedef long long LL;
#include<bits/stdc++.h>
#include<unordered_set>

unordered_map<int, int> p;
int a[100010];
int b[100010];
int op[100010];
LL find(LL x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}
void merge(LL x, LL y)
{
    LL px = find(x);
    LL py = find(y);
    p[px] = py;
}
void YD()
{
    int n;
    cin >> n;
    p.clear();
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i] >> b[i];
        cin >> op[i];
        p[a[i]] = a[i];
        p[b[i]] = b[i];
    }
    for (int i = 1; i <= n; i++)
    {
        if (op[i])
            merge(a[i], b[i]);
    }
    for (int i = 1; i <= n; i++)
    {
        if (!op[i])
        {
            LL pa = find(a[i]);
            LL pb = find(b[i]);
            if (pa == pb)
            {
                cout << "NO" << endl;
                return;
            }
        }
    }
    cout << "YES" << endl;


}
int main() {
    int t=1;
    cin >> t;
    while (t--)
    {
        YD();
    }

}
View Code
复制代码

离散化代码:

使用新数组:

复制代码
#include <iostream>
using namespace std;
typedef long long LL;
#include<bits/stdc++.h>
#include<unordered_set>

LL p[200010];
int a[200010];
int b[200010];
int op[200010];
LL find(LL x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}
void merge(LL x, LL y)
{
    LL px = find(x);
    LL py = find(y);
    p[px] = py;
}
void YD()
{
    int n;
    cin >> n;

    vector<LL> nums;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i] >> b[i];
        cin >> op[i];
        nums.push_back(a[i]);
        nums.push_back(b[i]);
    }
    sort(nums.begin(), nums.end());
    vector<LL> numm;
    for (int i = 0; i < nums.size(); i++)
    {
        if (i == 0 || nums[i] != nums[i - 1])
            numm.push_back(nums[i]);
    }
    nums = move(numm);

    for (int i = 0; i < nums.size(); i++) p[i] = i;

    for (int i = 1; i <= n; i++)
    {
        if (op[i])
        {
            auto index1 = lower_bound(nums.begin(), nums.end(), a[i])-nums.begin();
            auto index2 = lower_bound(nums.begin(), nums.end(), b[i]) - nums.begin();
            merge(index1, index2);
        }
            
    }
    for (int i = 1; i <= n; i++)
    {
        if (!op[i])
        {
            auto index1 = lower_bound(nums.begin(), nums.end(), a[i]) - nums.begin();
            auto index2 = lower_bound(nums.begin(), nums.end(), b[i]) - nums.begin();
            LL pa = find(index1);
            LL pb = find(index2);
            if (pa == pb)
            {
                cout << "NO" << endl;
                return;
            }
        }
    }
    cout << "YES" << endl;


}
int main() {
    int t=1;
    cin >> t;
    while (t--)
    {
        YD();
    }

}
View Code
复制代码

使用unique:

复制代码
#include <iostream>
using namespace std;
typedef long long LL;
#include<bits/stdc++.h>
#include<unordered_set>

LL p[200010];
int a[100010];
int b[100010];
int op[100010];
LL find(LL x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}
void merge(LL x, LL y)
{
    LL px = find(x);
    LL py = find(y);
    p[px] = py;
}
void YD()
{
    int n;
    cin >> n;

    vector<LL> nums;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i] >> b[i];
        cin >> op[i];
        nums.push_back(a[i]);
        nums.push_back(b[i]);
    }
    sort(nums.begin(), nums.end());
    auto it = unique(nums.begin(), nums.end());
    int nn = it - nums.begin();

    for (int i = 0; i < nn; i++) p[i] = i;

    for (int i = 1; i <= n; i++)
    {
        if (op[i])
        {
            auto index1 = lower_bound(nums.begin(), nums.begin()+nn, a[i])-nums.begin();
            auto index2 = lower_bound(nums.begin(), nums.begin()+nn, b[i]) - nums.begin();
            merge(index1, index2);
        }
            
    }
    for (int i = 1; i <= n; i++)
    {
        if (!op[i])
        {
            auto index1 = lower_bound(nums.begin(), nums.begin()+nn, a[i]) - nums.begin();
            auto index2 = lower_bound(nums.begin(), nums.begin()+nn, b[i]) - nums.begin();
            LL pa = find(index1);
            LL pb = find(index2);
            if (pa == pb)
            {
                cout << "NO" << endl;
                return;
            }
        }
    }
    cout << "YES" << endl;


}
int main() {
    int t=1;
    cin >> t;
    while (t--)
    {
        YD();
    }

}
View Code
复制代码

4、AcWing 239. 奇偶游戏 

离散化+前缀和思想+记录距离(模2)

复制代码
#include <iostream>
using namespace std;
typedef long long LL;
#include<bits/stdc++.h>
#include<unordered_set>

unordered_map<int, int> mmap;
int p[10010];
int d[10010];
int find(int x)
{
    if (p[x] != x)
    {
        int opx = p[x];
        p[x] = find(p[x]);
        d[x] = d[x] + d[opx];
        d[x] %= 2;
    }
    return p[x];
}
void merge(int x, int y, bool odd)
{
    int px = find(x);
    int py = find(y);
    if (d[x] == d[y])
    {
        if (odd)
        {
            p[px] = py;
            d[px] = 1;
        }
        else
        {
            p[px] = py;
            d[px] = 0;
        }
    }
    else
    {
        if (odd)
        {
            p[px] = py;
            d[px] = 0;
        }
        else
        {
            p[px] = py;
            d[px] = 1;
        }
    }
}
void YD()
{
    int n;
    cin >> n;
    mmap.clear();

    int index = 1;
    int m;
    cin >> m;
    for (int i = 1; i <= m; i++)
    {
        int x, y;
        string op;
        cin >> x >> y >> op;
        x--;
        if (!mmap.count(x))
        {
            mmap[x] = index;
            p[index] = index;
            d[index] = 0;
            index++;
        }
        if (!mmap.count(y))
        {
            mmap[y] = index;
            p[index] = index;
            d[index] = 0;
            index++;
        }
        int px = find(mmap[x]);
        int py = find(mmap[y]);
        if (px == py)
        {
            if (op == "even" && d[mmap[x]] != d[mmap[y]])
            {
                cout << i - 1 << endl;
                return;
            }
            if (op == "odd" && d[mmap[x]] == d[mmap[y]])
            {
                cout << i - 1 << endl;
                return;
            }

        }
        merge(mmap[x], mmap[y], op == "odd");
    }
    cout << m << endl;
    


}
int main() {
    int t=1;
    //cin >> t;
    while (t--)
    {
        YD();
    }

}
View Code
复制代码

 

posted @   80k  阅读(65)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示