[qbzt寒假] 并查集

并查集:
\(Kruscal\),\(Tarjan\)求\(LCA\)

分类并查集:食物链,团伙(敌人的敌人是我的朋友)

带权并查集:\(SDOI2016\)齿轮(可用

int father(int x) {
    return fa[x]==x?x:fa[x]=father(f[x]);
}

Luogu3101 滑雪等级[]

建边:任意相邻两格子之间建边,权值为海拔差

将边排序,从小往大一个一个往里加,当一个并查集内部有起点,并且大小(点数)>=T,这里面所有的起点的D=最后加入的边

#include<bits/stdc++.h>

using namespace std;

inline int read() {
    int ans=0;
    char last=' ',ch=getchar();
    while(ch>'9'||ch<'0') last=ch,ch=getchar();
    while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
    if(last=='-') ans=-ans;
    return ans;
}

int m,n,T,ecnt;
int a[520][521],siz[500100],fa[500100];
long long LSY[500100];
struct node {
    int u,v,w;
}e[500100];

int ykyk(int x,int y) {
    return (x-1)*n+y;
}

void insert(int x,int y,int dis) {
    ecnt++;
    e[ecnt].u=x;
    e[ecnt].v=y;
    e[ecnt].w=dis;
}

bool cmp(node a,node b) {
    return a.w<b.w;
}

int find(int x) {
    if(fa[x]==x) return x;
    int p=fa[x];
    fa[x]=find(fa[x]);
    if(LSY[x]==-1) LSY[x]=LSY[p];
    return fa[x];
}

int main() {
    m=read();
    n=read();
    T=read();
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
            a[i][j]=read();
    for(int i=1;i<=m;i++)
        for(int j=1;j<n;j++)
            insert(ykyk(i,j),ykyk(i,j+1),abs(a[i][j]-a[i][j+1]));
    for(int i=1;i<m;i++)
        for(int j=1;j<=n;j++)
            insert(ykyk(i,j),ykyk(i+1,j),abs(a[i][j]-a[i+1][j]));
    for(int i=1;i<=n*m;i++) fa[i]=i,siz[i]=1;
    sort(e+1,e+ecnt+1,cmp);
    memset(LSY,-1,sizeof(LSY));
    for(int i=1,p,q;i<=ecnt;i++) {
        p=find(e[i].u);q=find(e[i].v);
        if(p!=q) {
            if(siz[p]+siz[q]>=T) {
                if(siz[p]<T)
                    LSY[p]=e[i].w;
                if(siz[q]<T)
                    LSY[q]=e[i].w;
            }
            if(siz[q]<siz[p]) swap(p,q);
            fa[p]=q;
            siz[q]+=siz[p];
        }
    }
    long long LYT=0;
    for(int i=1,k;i<=m*n;i++) {
        k=read();

        if(k) {
            find(i);
            LYT+=LSY[i];
        }
    }
    printf("%lld\n",LYT);
    return 0;
}

BZOJ 1015 星球大战

很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系。某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球。这些星球通过特殊的以太隧道互相直接或间接地连接。
但好景不长,很快帝国又重新造出了他的超级武器。凭借这超级武器的力量,帝国开始有计划地摧毁反抗军占领的星球。由于星球的不断被摧毁,两个星球之间的通讯通道也开始不可靠起来。现在,反抗军首领交给你一个任务:给出原来两个星球之间的以太隧道连通情况以及帝国打击的星球顺序,以尽量快的速度求出每一次打击之后反抗军占据的星球的连通快的个数。(如果两个星球可以通过现存的以太通道直接或间接地连通,则这两个星球在同一个连通块中)。

反过来考虑,一个一个星球往上加

把所有询问离线下来,倒着做。

POJ 1703 Find them,Catch them[]

建\(2n\)个点,前\(n\)个点表示在第一个帮派中,后\(n\)个表示在第二个帮派中

如果(1,2)不在同一个帮派中,将第一个帮派中的第一个人和第二个帮派第二个人,第一个帮派中的第二个人和第二个帮派第一个人连起来

敌人的敌人就是朋友

\(D\) \(a\) \(b\) 连接\((a,b+n)(a+n,b)\)

\(A\) \(a\) \(b\) 如果\((a,b) (a+n,b+n)\)在同一个并查集,则是同一个帮派

如果\((a,b+n)(b,a+n)\)在同一个并查集,则是不同帮派

其余的不能确定

POJ 1182 食物链[]

动物王国中有三类动物\(A,B,C\),这三类动物的食物链构成了有趣的环形。\(A\)吃\(B\),
\(B\)吃\(C\),\(C\)吃\(A\)。
现有\(N\)个动物,以\(1-N\)编号。每个动物都是\(A,B,C\)中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这\(N\)个动物所构成的食物链关系进行描述:
第一种说法是"1 \(X\) $ Y\(",表示\)X\(和\)Y$是同类。
第二种说法是"2 \(X\) \(Y\)",表示\(X\)吃\(Y\)。
此人对\(N\)个动物,用上述两种说法,一句接一句地说出\(K\)句话,这\(K\)句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1)当前的话与前面的某些真的话冲突,就是假话;
2)当前的话中\(X\)或\(Y\)比\(N\)大,就是假话;
3)当前的话表示\(X\)吃\(X\),就是假话。
你的任务是根据给定的\(N(1<= N <= 50,000)\)和\(K\)句话\((0 <= K <= 100,000)\),输出假话的总数。

开三个并查集:

\(\color{Yellow}同类\ \ \ \color{Red}捕食\):

如果\(a\)和\(b\)是同类,则a的三个节点\((a,a+n,a+2n)\)分别指向\(b\)的对应的三个节点\((b,b+n,b+2n)\)

如果\(a\)吃\(b\),则\(a\)的三个节点分别指向对应\(b\)的三个节点的下一个节点

#include<bits/stdc++.h>

using namespace std;

inline int read(){
    int ans=0;
    char last=' ',ch=getchar();
    while(ch>'9'||ch<'0') last=ch,ch=getchar();
    while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
    if(last=='-') ans=-ans;
    return ans;
}const int mxn=50010;

int n,k;
int cnt,ans;
int fa[mxn*3];

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

int main() {
    n=read();
    k=read();
    for(int i=1;i<=3*n;i++) fa[i]=i;
    for(int i=1,op,x,y;i<=k;i++) {
        op=read();
        x=read();
        y=read();
        if(y>n||x>n) {
            ans++;
            continue;
        }
        if(op==2&&x==y) {
            ans++;
            continue;
        }
        if(op==1) {
            if(find(x)==find(y+n)||find(x)==find(y+2*n)) {
                ans++;
                continue;
            }
            int xx=find(x),yy=find(y);
            fa[xx]=yy;
            int _x=find(x+n),_y=find(y+n);
            fa[_x]=_y;
            int x_=find(x+2*n),y_=find(y+2*n);
            fa[x_]=y_;
        }
        if(op==2) {
            if(find(x+2*n)==find(y)||find(x)==find(y)) {
                ans++;
                continue;
            }
            int xx=find(x),yy=find(y);
            int _x=find(x+n),_y=find(y+n);
            int x_=find(x+2*n),y_=find(y+2*n);
            fa[yy]=_x;
            fa[xx]=y_;
            fa[x_]=_y;
        }

    }
    printf("%d",ans);
    return 0;
}

并查集相关:

\(BZOJ\) \(2303\) \([Apio2011]\) 方格染色

\(BZOJ\) \(1854\) \([SCOI2010]\) 游戏

带权并查集:

\(HDU\)\(3038\) \(How\) \(Many\) \(Answers\) \(Are\) \(Wrong\)

\(HihoCoder\) \(1515\) 分数调查

原文地址:https://www.cnblogs.com/zhuier-xquan/p/12257437.html

时间: 2024-10-01 22:59:36

[qbzt寒假] 并查集的相关文章

并查集—分离集合森林实现

并查集总结 今天总结一下并查集,这个完了之后,寒假学的数据结构基础的模板类的题目差不多就完了,对于模板题,敲上10遍.20遍.30遍,那么模板就不是模板,就成为了你自己的东西,就好像 A+B 一辈子也忘不了,以后每天敲一遍模板题,加深对模板的理解. 并查集,一般使用的是 数组实现.树实现,其中数组实现时间复杂度较高,树实现也就是分离集合森林 查找.合并的时间复杂度不会 超过 O(log2n) n个人,m对有亲戚关系的 10 7 1 2 2 3 2 4 3 4 5 6 6 7 8 9 初始化:{1

工作安排加强版(神奇的并查集)

题目描述: 为了维持农场的运转,约翰必须打工赚钱.他接到了 N 份工作,每份工作恰好占用他一天的时间.约翰从第一天开始工作,他可以任意安排这些工作的顺序,第 i 份工作有 Pi 的报酬,但必须在第 Di 天结束之前完成.在截止日期后完成的工作没有报酬.请帮助约翰规划每天的工作,使得他赚到的钱最多. 1 ≤ N ≤ 105  , 1 ≤ Di ,Pi ≤ 109     这题就恶心在数据范围额. 解题过程: 1.这题寒假做贪心专题的时候做到过,贪心策略大致还记得,就是先按照报酬P从大到小排序,然后

并查集的小节

寒假就看了并查集的视频,可是一直没怎么用了,现在终于还是拾起,看了这方面的视频还是很好理解的: 看了挑战程序竞赛这本后,感觉大佬的思路就是简单,高效,可以首先写几个函数,分部实现各个功能, 按照思路: 1,格式化数组: 2,定义查找函数,便于找根: 3,定义联合函数,使需要联合的函数联合在一起, 4,定义判断函数,判断是否属于同一根 1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 using

CodeForces 745C Hongcow Builds A Nation 并查集

题意: 给了你n个城市 m条边 k个政府 每个政府管辖的区域内不能和其他政府的区域有相连 即政府之间不存在路径 问你在维护这种关系的同时 最多再加多少条边 思路: 先找出来每个联通块 再找出来没有归属的孤立的点 把他们都放到最大的联通块里 然后每个联通块之间的点两两连边是n*(n-1)/2条边 最后算出来的ans-m就好了 (看别人的博客学了一个max_element 1 #include<bits/stdc++.h> 2 #define cl(a,b) memset(a,b,sizeof(a

并查集(个人模版)

并查集: 1 int find(int a) 2 { 3 int r=a; 4 while(f[r]!=r) 5 r=f[r]; 6 int i=a; 7 int j; 8 while(i!=r) 9 { 10 j=f[i]; 11 f[i]=r; 12 i=j; 13 } 14 return r; 15 } 16 int merge(int a,int b) 17 { 18 int A,B; 19 A=find(a); 20 B=find(b); 21 if(A!=B) 22 { 23 f[B

并查集应用

题目描述: One way that the police finds the head of a gang is to check people's phone calls. If there is a phone call between A and B, we say that A and B is related. The weight of a relation is defined to be the total time length of all the phone calls

【bzoj3674】 可持久化并查集加强版

http://www.lydsy.com/JudgeOnline/problem.php?id=3674 (题目链接) 题意 维护并查集3个操作:合并:回到完成第k个操作后的状态:查询. Solution 其实就是用主席树的叶子节点维护并查集的可持久化数组fa[]. 细节 终于认识到了按秩合并的强大,单纯写个路径压缩Re飞,写了路径压缩+按秩合并比单纯的按秩合并每快多少→_→ 代码 // bzoj3674 #include<algorithm> #include<iostream>

BZOJ1015[JSOI2008]星球大战starwar[并查集]

1015: [JSOI2008]星球大战starwar Time Limit: 3 Sec  Memory Limit: 162 MBSubmit: 5253  Solved: 2395[Submit][Status][Discuss] Description 很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系.某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球.这些星球通过特殊的以太隧道互相直接或间接地连接. 但好景不长,很快帝国又重

HDU 5606 tree 并查集

tree 把每条边权是1的边断开,发现每个点离他最近的点个数就是他所在的连通块大小. 开一个并查集,每次读到边权是0的边就合并.最后Ans?i??=size[findset(i)],size表示每个并查集根的size Ans_i=size[findset(i)],sizeAns?i??=size[findset(i)],size表示每个并查集根的sizesize. #include<cstdio> #include<cstring> #include<algorithm>