并查集babiQ

53 篇文章 0 订阅
9 篇文章 0 订阅

AcWing 237. 程序自动分析
题目链接
在这里插入图片描述
典型的并查集题目,利用题目约束的传递性做题。
此题既不需要带权,也不需要拓展域。
注意题目范围,需要先进行离散化处理(并查集的题目一般都需要进行离散化处理)

离散化

void my_read(int n){
    int i,j,e;
    total = 0;
    for(int k=1;k<=n;k++){
        scanf("%d%d%d",&i,&j,&e);
        query[k] = (Query){i,j,e};
        if(e){
            a[++total] = i;
            a[++total] = j;
        }
    }
    sort(a+1,a+1+total);
    total = unique(a+1,a+1+total) - a - 1;
    // cout << "total=" << total << endl;
    // for(int k=1;k<=total;k++)   cout << a[k] << " ";
    // cout << "\n";
}

思路:先处理所有具有传递性的等于条件,再依次判断不等于的条件是否成立。

#include <bits/stdc++.h>

using namespace std;

const int N = 100010;
struct Query{
    int i;
    int j;
    int e;
}query[N];

int fa[2*N];
int a[2*N];
int total;

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

void my_read(int n){
    int i,j,e;
    total = 0;
    for(int k=1;k<=n;k++){
        scanf("%d%d%d",&i,&j,&e);
        query[k] = (Query){i,j,e};
        if(e){
            a[++total] = i;
            a[++total] = j;
        }
    }
    sort(a+1,a+1+total);
    total = unique(a+1,a+1+total) - a - 1;
}

void solve(){
    int n;
    cin >> n;
    my_read(n);
    
    bool f = 1;
    for(int i=1;i<=total;i++)   fa[i] = i;
    for(int k=1;k<=n;k++){
        int i = query[k].i,j = query[k].j,e = query[k].e;
        if(!e)  continue;
        int x = lower_bound(a+1,a+total+1,i) - a;
        int y = lower_bound(a+1,a+total+1,j) - a;
        if(i==j){
            continue;
        }
        if(e==1){
            int rx = find(x);
            int ry = find(y);
            fa[rx] = ry;
        }
    }
    for(int k=1;k<=n;k++){
        int i = query[k].i,j = query[k].j,e = query[k].e;
        if(e)  continue;
        
        int x = lower_bound(a+1,a+total+1,i) - a;
        int y = lower_bound(a+1,a+total+1,j) - a;
        if(i==j){
            f = 0;
            break;
        }
        if(a[x]!=i || a[y]!=j)  continue;
        int rx = find(x);
        int ry = find(y);
        if(rx==ry){
            f = 0;
            break;
        }
    }
    
    if(f)   puts("YES");
    else    puts("NO");
}

int main()
{
    int t;
    cin >> t;
    while(t--){
        solve();
    }
    return 0;
}

AcWing 238. 银河英雄传说
题目链接
在这里插入图片描述
典型带权并查集,记录每个结点到根结点的距离。
注意find函数的写法,很容易写错,理解了之后基本上就固定模式了。

/*d数组是边权*/
int find(int x){
    if(fa[x]==x)    return x;
    int f = find(fa[x]);
    d[x] += d[fa[x]];
    return fa[x] = f;
}

完整代码:

#include <bits/stdc++.h>

using namespace std;

const int N = 300005;
int fa[N];
int sz[N];
int d[N];

int find(int x){
    if(fa[x]==x)    return x;
    int f = find(fa[x]);
    d[x] += d[fa[x]];
    return fa[x] = f;
}

void merge(int x,int y){
    int rx = find(x),ry = find(y);
    if(rx==ry)  return;
    
    fa[rx] = ry;
    d[rx] += sz[ry];    //d数组表示到树根的距离.
    sz[ry] += sz[rx];
}

bool same(int x,int y){
    return find(x)==find(y);
}

int main()
{
    for(int i=1;i<N;i++)    fa[i] = i,sz[i] = 1;
    memset(d,0,sizeof d);
    int t;
    cin >> t;
    char s[5];
    int a,b;
    while(t--){
        scanf("%s%d%d",s,&a,&b);
        
        if(*s=='M'){
            merge(a,b);
        }else{
            if(a==b){
                puts("0");
                continue;
            }
            
            if(same(a,b)){
                printf("%d\n",abs(d[a]-d[b])-1);
            }else{
                puts("-1");
            }
        }
    }
    return 0;
}

AcWing 239. 奇偶游戏
题目链接
在这里插入图片描述
分析:
记sum[i]为前i个数的1的个数(也就是前缀和)。
若[l~r]为奇数:则sum[r] 和 sum[l-1]奇偶性不相同。
若[l~r]为偶数:则sum[r] 和 sum[l-1]奇偶性相同。
因此需要看sum[l-1] 与 sun[r] 是否产生矛盾。
有两种方法:

  1. 边带权并查集:记录每个点与父结点的奇偶性关系,相同为0,不同为1。(仔细想想,这点正是异或运算律)因此可以借助异或运算来判断同一集合中不同的两点的奇偶性。在进行路径压缩的时候,根据异或运算,可以得出与根节点的奇偶性关系。
    若p和q在同一集合中,则check d[x] ^d[y];若不在,则加入同一集合, d[p] = d[x] ^ d[y]^ query[i].d;
  2. 拓展域并查集:把每个结点x 分为x_odd与x_even。这个思路相对简单,代码量较大。

解1:边带权并查集:

#include <bits/stdc++.h>
#include <functional>

#define pi acos(1.0)
#define fi first
#define se second
#define INF 0x3f3f3f3f

using namespace std;

typedef long long ll;
const int N = 10005,M = 2*N;
const int MOD = 1e9+7;

struct Query{
    int l,r;
    int d;      // 0:偶  1:奇
}query[N];
int fa[N];
int a[N];
int total;
int d[N];

int find(int x){
    if(x==fa[x])    return x;
    int f = find(fa[x]);
    d[x] ^= d[fa[x]];
    return fa[x] = f;
}

void myread(int n){
    total = 0;
    int l,r;
    string s;
    int d;
    for(int i=1;i<=n;i++){
        cin >> l >> r >> s;
        if(s=="even")   d = 0;
        else    d = 1;
        query[i] = (Query){l,r,d};

        a[++total] = l-1;
        a[++total] = r;
    }
    sort(a+1,a+1+total);
    total = unique(a+1,a+1+total) - a - 1;
}

void solve(){
    int n,m;
    cin >> m >> n;
    myread(n);
    for(int i=1;i<=total;i++)   fa[i] = i;
    int ans = n;
    for(int i=1;i<=n;i++){
        int x = lower_bound(a+1,a+1+total,query[i].l-1) - a;
        int y = lower_bound(a+1,a+1+total,query[i].r) - a;
        int p = find(x);
        int q = find(y);

        if(p!=q){
            fa[p] = q;
            d[p] = d[x]^d[y]^query[i].d;
        }else{
            // check
            if(query[i].d != (d[x]^d[y])){
                ans = i-1;
                break;
            }
        }
    }

    printf("%d\n",ans);
}

int main()
{   
    std::ios::sync_with_stdio(false);std::cin.tie(nullptr);
    int T = 1;
    // cin >> T;
    while(T--){
        solve();
    }
    return 0;
}

解法2:拓展域并查集

#include <bits/stdc++.h>
#include <functional>

#define pi acos(1.0)
#define fi first
#define se second
#define INF 0x3f3f3f3f
#define int long long

using namespace std;

typedef long long ll;
const int N = 10005,M = 2*N;
const int MOD = 1e9+7;

int a[2*N],fa[2*N];
struct Query{
    int l,r,d;
}query[N];
int total = 0;

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

void myread(int m){
    string s;
    int l,r;
    int d;
    for(int i=1;i<=m;i++){
        cin >> l >> r >> s;
        if(s=="even")   d = 0;
        else    d = 1;
        query[i] = (Query){l-1,r,d};

        a[++total] = l-1;
        // a[++total] = l-1+m;
        a[++total] = r;
        // a[++total] = r+m;
    }
    sort(a+1,a+1+total);
    total = unique(a+1,a+1+total) - a - 1;
}

void solve(){
    int n,m;
    cin >> n >> m;
    myread(m);

    for(int i=1;i<=2*total;i++)   fa[i] = i;
    int ans = m;
    for(int i=1;i<=m;i++){
        int l = query[i].l,r = query[i].r,d = query[i].d;

        int x_odd = lower_bound(a+1,a+1+total,l) - a;
        // int x_even = lower_bound(a+1,a+1+total,l+total) - a;
        int y_odd = lower_bound(a+1,a+1+total,r) - a;
        // int y_even = lower_bound(a+1,a+1+total,r+total) - a;
        int x_even = x_odd + total;
        int y_even = y_odd + total;

        int fa_x_odd = find(x_odd),fa_x_even = find(x_even),fa_y_odd = find(y_odd),fa_y_even = find(y_even);

        if(d){
            if(fa_x_odd==fa_y_odd){
                ans = i-1;
                break;
            }

            fa[fa_x_odd] = fa_y_even;
            fa[fa_x_even] = fa_y_odd;
        }else{
            if(fa_x_odd==fa_y_even){
                ans = i-1;
                break;
            }

            fa[fa_x_odd] = fa_y_odd;
            fa[fa_x_even] = fa_y_even;
        }
    }
    cout << ans << endl;
    return;
}

signed main()
{   std::ios::sync_with_stdio(false);std::cin.tie(nullptr);
    int T = 1;
    while(T--){
        solve();
    }
    return 0;
}

AcWing 240. 食物链
题目链接
很经典的一道题,两种并查集都可做,注意使用拓展域的并查集时一定要考虑全面,所有域的信息都有用。

// 拓展域

/*
    1.同类关系     x_1 = x
    2.捕食关系     x_2 = x + n 
    3.天敌关系     x_3 = x + n + n      目前来看:第三种关系貌似不需要
*/

#include <bits/stdc++.h>

using namespace std;

const int N = 50005;
int a[3*N],fa[3*N];

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

void solve(){
    int n,k;
    cin >> n >> k;
    for(int i=1;i<=3*n;i++)   fa[i] = i;
    
    int ans = 0;
    for(int i=1;i<=k;i++){
        int d,x,y;
        cin >> d >> x >> y;
        
        if(x>n || y>n){
            ans++;
            continue;
        }else if(d==2 && x==y){
            ans++;
            continue;
        }
        
        int x_1 = x,x_2 = x + n,x_3 = x + n + n;
        int y_1 = y,y_2 = y + n,y_3 = y + n + n;
        if(d==1){
            if(find(x_1)==find(y_2) || find(x_2)==find(y_1)){
                ans++;
                continue;
            }
            
            fa[find(x_1)] = find(y_1);
            fa[find(x_2)] = find(y_2);
            fa[find(x_3)] = find(y_3);
        }else{
            if(find(x_1)==find(y_1) || find(y_2)==find(x_1)){
                ans++;
                continue;
            }
            
            fa[find(x_2)] = find(y_1);  //在x的捕食域中加入y的同类域
            fa[find(y_2)] = find(x_3);  //在y的捕食域中加入x的天敌域
            fa[find(y_3)] = find(x_1);  //在y的天敌域中加入x的同类域
        }
    }
    cout << ans << endl;
    return;
}

int main()
{
    std::ios::sync_with_stdio(false);std::cin.tie(nullptr);
    int T = 1;
    // cin >> T;
    while(T--){
        solve();
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辽宇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值