并查集总结

并查集无疑是一个很基础的算法, 它的主要意义是快速地插入或询问两个数之间的关系。

poj1112 小胖的奇偶

刚接触的话这道题的确是相当的厉害啊!

维护一段区间的奇偶性, 也就是从Y 到 X 这段是0 还是1 。

设有关系 f(x, y),  f(y, z), 那么 f(x, z) == (f(x, y) + f(y, z))%2;

这里的办法是每个点都开两个点f(x),  f(x + n)

然后因为n很大所以要再写一个hash就好了

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define mod 6000
using namespace std;
int n, m, map[mod + 100], fat[mod * 5];
int hash(int x){
    int t = x % mod;
    while(map[t] != -1 && map[t] != x)t = (t + 1) % mod;
    map[t] = x; return t;
}
int find(int x){
    if(!fat[x]) return x;
    return fat[x] = find(fat[x]);    
}
void uu(int a, int b){
    int x = find(a), y = find(b);
    if(x != y)fat[x] = y;    
}
int main()
{
    scanf("%d%d", &n, &m); int i;
    memset(map, -1, sizeof(map));
    int a, b; char s[10];
    for(i = 1; i <= m; i ++){
        scanf("%d%d%s", &a, &b, s);
        int flag = 0;if(s[0] == 'o')flag = 1;
        a = hash(a - 1); b = hash(b);
        if(flag){
            if(find(a) == find(b)) break;
            uu(a, b + mod + 1); uu(a + mod + 1, b);    
        }
        else{
            if(find(a) == find(b + mod + 1))break;
            uu(a, b); uu(a + mod + 1, b + mod + 1);    
        }
    }printf("%d\n", i - 1);
   // system("pause");
    return 0;
}

vj 1531 食物链

最经典的并查集了吧, 和刚才那道题类似! 就是开三个点就好了

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define MAXN 50005
using namespace std;
int n, k, fat[MAXN * 3], ans;
int find(int x){
    if(!fat[x])return x;
    return (fat[x] = find(fat[x]));    
}
void uu(int x, int y){
    x = find(x), y = find (y);
    if(x != y)fat[y] = x;   
}
int main()
{
    scanf("%d%d", &n, &k);
    int d, x, y;
    for(int i = 1; i <= k; i ++){
        scanf("%d%d%d", &d, &x, &y);
        if(x > n || y > n || (d == 2 && x == y)){ans ++; continue;}
        if(d == 1){
            if(find(x) == find(y + n) || find(x) == find(y + n + n)){ans ++; continue;}
            uu(x, y); uu(x + n, y + n); uu(x + n + n, y + n + n);    
        } 
        else{
            if(find(x) == find(y) || find(x) == find(y + n + n)){ans ++; continue;}
            uu(x, y + n); uu(x + n, y + n + n); uu(x + n + n, y);    
        }
    }cout<<ans<<endl;
//    system("pause");
    return 0;
}

所以又写了个偏移向量的

我们用一个dir[]数组表示每一个物种与他上级的关系,我们很容易能发现

利用a,b b,c的关系,可以推导出a,c的关系!

我定义如果i和上级是同物种,dir[i] = 0

i吃上级,dir[i] = 1;

i被上级吃,dir[i] = 2;

为什么这么定义呢?(其实你也可以把值改变,但关系传递时你就慢慢写if来判断吧)

我的合并操作还是把a所在树当做b的子树,那么a的上级与b的关系也可以根据a,b的关系&&a和上级的关系推出

代码:

  1. #include <iostream>  
  2. #include <cstdio>  
  3. #include <cstdlib>  
  4. using namespace std;  
  5. #define MAX (50000+1)  
  6. int n,m,mot[MAX],dir[MAX],ans;  
  7. int find(int x)  
  8. {  
  9.     if ( x != mot[x] )  
  10.     {  
  11.         int fat = mot[x];  
  12.         mot[x] = find(mot[x]);  
  13.         dir[x] += dir[fat];  
  14.         dir[x] %= 3;  
  15.     }  
  16.     return mot[x];  
  17. }  
  18. void merge(int a,int b,int c)  
  19. {  
  20.     if ( a == mot[a] )  
  21.     {  
  22.         mot[a] = b;  
  23.         dir[a] = c;  
  24.     }  
  25.     else  
  26.     {  
  27.         mot[ mot[a] ] = b;  
  28.         dir[ mot[a] ] = ((c + 9) - dir[a]) % 3;  
  29.     }  
  30. }  
  31. int main()  
  32. {  
  33.     scanf("%d %d",&n,&m);   
  34.     for (int i = 1; i <= n; i++)  
  35.     {  
  36.         mot[i] = i;  
  37.     }  
  38.     int swi,a,b;  
  39.     for (int i = 1; i <= m; i++)  
  40.     {  
  41.         scanf("%d %d %d",&swi,&a,&b);  
  42.         if ( a > n || b > n )  
  43.         {  
  44.             ans++;  
  45.         }  
  46.         else if ( swi == 1 )  
  47.         {  
  48.             if ( find(a) == find(b) )  
  49.             {  
  50.                 if ( dir[a] != dir[b] )  
  51.                 {  
  52.                     ans++;  
  53.                 }  
  54.             }  
  55.             else  
  56.             {  
  57.                 merge(a,b,0);  
  58.             }  
  59.         }  
  60.         else  
  61.         {  
  62.             if ( find(a) == find(b) )  
  63.             {  
  64.                 if ( ( 1 + dir[b] ) % 3 != dir[a] )  
  65.                 {  
  66.                     ans++;  
  67.                 }  
  68.             }  
  69.             else  
  70.             {  
  71.                 merge(a,b,1);  
  72.             }  
  73.         }  
  74.     }  
  75.     cout<<ans;  
  76.     return 0;  


bzoj 1202

其实做过了前两题之后再看这道题就很显然了! 但我最开始还是错了。。说明要加强练习。

开两个数组一个记录一个点的根节点一个记录该点与根节点之间的关系。

进一步地了解了并查集: 在多个连通块中, l常数 的时间查询两个点是否在一个连通块内以及一个连通块中两个点的关系。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define MAXN 105
using namespace std;
int t, n, m, fat[MAXN], ff[MAXN];
int find(int x){
    if(fat[x] != x){
        int tmp = fat[x];
        fat[x] = find(fat[x]);
        ff[x] += ff[tmp];    
    }    return fat[x];
}
int main()
{
    scanf("%d", &t); while(t --){
        scanf("%d%d", &n, &m);
        for(int i = 0; i <= n; i ++)fat[i] = i;
        memset(ff, 0, sizeof(ff)); 
        bool flag = 1;
        for(int i = 1; i <= m; i ++){
            if(!flag)break;
            int a, b, x; scanf("%d%d%d", &a, &b, &x); a --;
            int fa = find(a), fb = find(b);
            if(fa != fb){
                fat[fb] = fa; ff[fb] = ff[a] - ff[b] + x;   
            }
            else if(ff[b] - ff[a] != x)flag = 0;
        }if(flag)puts("true");else puts("false");
    }    
    return 0;
}

hcs的题。。基本上和上一道一样!机智的hcs !!

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define MAXN 100005
using namespace std;
int n, m, k, ans, fat[MAXN], ff[MAXN];
int find(int x){
    if(fat[x] != x){
        int tmp = fat[x];
        fat[x] = find(fat[x]);
        ff[x] = (ff[x] + ff[tmp]) % k;
    }    return fat[x];
}
int main()
{
    scanf("%d%d%d", &n, &m, &k); k ++;
    for(int i = 0; i <= n; i ++)fat[i] = i;
    int c, a, b;
    for(int i = 1; i <= m; i ++){
        scanf("%d%d%d", &c, &a, &b);a --;
        int fa = find(a), fb = find(b);
        if(fa != fb){
            fat[fb] = fa; ff[fb] = (ff[a] - ff[b] + k + c) % k;
        }    
        else if(((ff[b] - ff[a] + k) % k) != c)ans ++;
    }double aa = (double)(m - ans) / (double)m;
    printf("%.5lf\n", aa);
    return 0;
}



内容概要:本文介绍了多种开发者工具及其对开发效率的提升作用。首先,介绍了两款集成开发环境(IDE):IntelliJ IDEA 以其智能代码补全、强大的调试工具和项目管理功能适用于Java开发者;VS Code 则凭借轻量级和多种编程语言的插件支持成为前端开发者的常用工具。其次,提到了基于 GPT-4 的智能代码生成工具 Cursor,它通过对话式编程显著提高了开发效率。接着,阐述了版本控制系统 Git 的重要性,包括记录代码修改、分支管理和协作功能。然后,介绍了 Postman 作为 API 全生命周期管理工具,可创建、测试和文档化 API,缩短前后端联调时间。再者,提到 SonarQube 这款代码质量管理工具,能自动扫描代码并检测潜在的质量问题。还介绍了 Docker 容器化工具,通过定义应用的运行环境和依赖,确保环境一致性。最后,提及了线上诊断工具 Arthas 和性能调优工具 JProfiler,分别用于生产环境排障和性能优化。 适合人群:所有希望提高开发效率的程序员,尤其是有一定开发经验的软件工程师和技术团队。 使用场景及目标:①选择合适的 IDE 提升编码速度和代码质量;②利用 AI 编程助手加快开发进程;③通过 Git 实现高效的版本控制和团队协作;④使用 Postman 管理 API 的全生命周期;⑤借助 SonarQube 提高代码质量;⑥采用 Docker 实现环境一致性;⑦运用 Arthas 和 JProfiler 进行线上诊断和性能调优。 阅读建议:根据个人或团队的需求选择适合的工具,深入理解每种工具的功能特点,并在实际开发中不断实践和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值