[Method summary] disjoint-set


I, on the disjoint-set

1. Definitions

Disjoint-set (Disjoint-Set) is a dynamically maintain a set of a plurality of non-overlapping, and supports combined with query a data structure of the two operations.

2. Basic Operations

1. Combined (of Union / the Merge) . 1 : two sets combined.
2. Query (Find / Get): query element belongs to the set.
The actual operation, we will use a point to represent the entire collection, that is a root element (can be understood as the father).

3. realization

We create an array fa[ ]or pre[ ]represent a disjoint-set, fa[i]represents the iparent node.
Initialization: every point is a collection, so the parent is their own fa[i]=i
query: each node constantly looking for their parent, if the parent node at this time is their own, then the collection point for the root node, returns that point.
Review: merging the two sets combined root only two sets, i.e. fa[RootA]=RootB, where RootA, RootBis the root of the two elements.

Path Compression:
In fact, we only care about what the root node in the query process is not concerned with this tree form (with the exception of some of the questions). Therefore, we can query each time point visited all point to the roots, so the method is called path compression, a single operation complexity is \ (O (logN) \) .
In conjunction with the FIG better consumption (Pictured state compression process):
Picture 3.png


Second, code implementation

Initialization template:

for(int i=1;i<=n;i++) pre[i]=i;

Template query (with path compression):

int Find(int x){
    if(x==pre[x]) return x;
    return pre[x]=Find(pre[x]);
}

Merge template:

void merge(int x,int y){
    int fx=Find(x),fy=Find(y);
    if(fx!=fy) pre[fx]=fy;
}
//主函数内
merge(a,b);

Third, some of the examples

Example 1: P1551 relatives

Template title. Here on hold code ...
it is also a template question: P2814 genealogy , recommended map (STL).

Example 2: P1536 village

Seeking to sell all the way how many roads to be built China Unicom.
We first point of the road has been built merger, if it is needed to build the road, then the number of points of the set of all must be greater than 1.
As long as we find out if a single point to form a collection that is a point of asking whether the parent node to itself.
It is noteworthy that eventually merged into the parent node of a root node is set in itself, we output the final answer to lose.
Code:

#include <bits/stdc++.h>
using namespace std;
int pre[1000001],n,m,ans;
inline int Find(int x){
    return pre[x]==x?x:pre[x]=Find(pre[x]);
}
inline void Union(int x, int y){
    int fx=Find(x),fy=Find(y);
    if(fx!=fy) pre[fx]=fy;
}
int main()
{
    while(scanf("%d",&n)&&n){
        ans=0;
        scanf("%d", &m);
        for(int i=1;i<=n;i++) pre[i]=i;
        for(int i=1,x,y;i<=m;i++){
            scanf("%d%d",&x,&y);
            Union(x,y); 
        }
        for(int i=1;i<=n;i++){
            if(Find(i)==i) ans++;
        }
        printf("%d\n",ans-1);
    }
    return 0;
}

Example 3: P1396 rescue

FBI! Open UP! See seek maximum minimum that we know will use half solved.
Along the way to go, we can half the degree of congestion, in determining the degree of congestion is feasible, is greater than all crowded mid sides are removed, the final judgment disjoint-set spoint and the tpoint is to Unicom.
Code:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define N 50050
using namespace std;
int l=INF,r=-1,n,m,s,t,ans;
int pre[N],x[N],y[N],cost[N];
inline int find(int x){return x==pre[x]?x:pre[x]=find(pre[x]);}
inline int check(int mid){
    for(int i=1;i<=n;i++) pre[i]=i;
    for(int i=1;i<=m;i++){
        if(cost[i]>mid) continue;
        int fx=find(x[i]),fy=find(y[i]);
        if(fx!=fy) pre[fx]=fy;
    }
    if(find(s)==find(t)) return 1;
    return 0;
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&x[i],&y[i],&cost[i]);
        l=min(l,cost[i]);r=max(r,cost[i]);
    }
    while(l<=r){
        int mid=(l+r)>>1;
        if(check(mid)){
            r=mid-1;
            ans=mid;
        }
        else l=mid+1;
    }
    printf("%d",ans);
    return 0;
}

Example. 4: P1621 set

Is greater than the number of all prime factors p are seeking out combined disjoint-set and finally solved.

#include<bits/stdc++.h>
#define N 100010
using namespace std;
int a,b,cmp,ans,cnt;
int pre[N],notp[N],p[N];
int Find(int x){
    if(x==pre[x]) return x;
    return pre[x]=Find(pre[x]);
}
inline int Union(int x,int y){
    int fx=Find(x),fy=Find(y);
    if(fx!=fy) pre[fx]=fy;
}
inline void E_prime(){
    notp[1]=1;
    for(int i=2;i<=b;i++){
        if(notp[i]) continue;
        for(int j=i*2;j<=b;j+=i){
            notp[j]=1;
        }
    }
    for(int i=cmp;i<=b;i++)
        if(not notp[i]) p[++cnt]=i;
}
int main()
{
    scanf("%d%d%d",&a,&b,&cmp);
    for(int i=a;i<=b;i++) pre[i]=i;
    E_prime();
    for(int i=1;i<=cnt;i++)
        for(int j=2;j*p[i]<=b;j++)
            if(j*p[i]>=a&&(j-1)*p[i]>=a)
                Union(p[i]*j,p[i]*(j-1));
    for(int i=a;i<=b;i++)
        if(pre[i]==i) ans++;
    printf("%d",ans);
    return 0;
}

例5:P4185 [USACO18JAN]MooTube

K and the right side from the large to the small sort, such a large K also meet the small K, to avoid duplication of search.
Code:

#include<bits/stdc++.h>
#define N 300010
using namespace std;
int n,q,fa[N],size[N],ans[N];
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline void Union(int x,int y){
    int fx=find(x),fy=find(y);
    if(fx==fy) return;
    size[fx]+=size[fy],fa[fy]=fx;
}
struct edge{
    int u,v,r;
}p[N];
struct node{
    int k,v,id;
}ask[N];
inline int CMP(edge a,edge b){return a.r>b.r;}
inline int cmp(node a,node b){return a.k>b.k;}
int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<n;i++)
        scanf("%d%d%d",&p[i].u,&p[i].v,&p[i].r);
    for(int i=1;i<=q;i++)
        scanf("%d%d",&ask[i].k,&ask[i].v),ask[i].id=i;
    for(int i=1;i<=n;i++){fa[i]=i;size[i]=1;}
    sort(p+1,p+n,CMP);
    sort(ask+1,ask+q+1,cmp);
    int pos=1;
    for(int i=1;i<=q;i++){
        while(pos<n&&p[pos].r>=ask[i].k){
            Union(p[pos].u,p[pos].v);
            pos++;
        }
        ans[ask[i].id]=size[find(ask[i].v)]-1;
    }
    for(int i=1;i<=q;i++)
        printf("%d\n",ans[i]);
    return 0;
}

Example. 6: P1197 [JSOI2008] Star Wars

Obtaining the number of questions asked after each communication block against the figures. Disjoint-set can merge operation, but the operation can not be separated, so we reverse process. Unicom not blown to point communication, and can count the number block at this time, followed by continued combined and blown to count the number of communication points at that time block.
Code:

#include<bits/stdc++.h>
#define N 400010
using namespace std;
int n,m,k,pre[N],first[N],nxt[N],go[N];
int poi[N],off[N],tot,from[N],sum,ans[N];
inline void add_edge(int u,int v){
    nxt[++tot]=first[u];
    first[u]=tot;
    go[tot]=v;
    from[tot]=u;
}
inline int Find(int x){
    return x==pre[x]?x:pre[x]=Find(pre[x]);
}
inline void Union(int x,int y){
    int fx=Find(x),fy=Find(y);
    if(fx!=fy) pre[fx]=fy;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) pre[i]=i;
    for(int i=1,x,y;i<=m;i++){
        scanf("%d%d",&x,&y);
        add_edge(x,y);
        add_edge(y,x);
    }
    scanf("%d",&k);
    for(int i=1;i<=k;i++){
        scanf("%d",&poi[i]);
        off[poi[i]]=1;//标记 
    }
    sum=n-k;//剩余的点 
    for(int i=1;i<=m<<1;i++)//双向边 
        if(!off[from[i]]&&!off[go[i]])//两个点都没被炸 
            if(Find(from[i])!=Find(go[i]))
                sum--,Union(from[i],go[i]);//合并,更新连通块个数 
    ans[k+1]=sum;//最后一个答案 
    for(int i=k;i>=1;i--){
        sum++;//恢复这个点后自己也算一个连通块,因此个数要+1 
        off[poi[i]]=0;//恢复 
        for(int e=first[poi[i]];e;e=nxt[e]){
            int v=go[e];
            if(!off[v]&&Find(poi[i])!=Find(v)){
                Union(poi[i],v);//合并 
                sum--; 
            }
        }
        ans[i]=sum;
    }
    for(int i=1;i<=k+1;i++)
        printf("%d\n",ans[i]);
    return 0;
}

: 7 cases bzoj2054 crazy bread

2054_1.jpg
We finally found a full head of color infected only with his last being infected with color, and therefore we handle backwards, then we need to maintain the already dyed bread will not be stained. From left to right scanning range, to find his father this point, at this time the father is not yet fill the node color (if the parent node is still in the interval), we put this point dyed and this points to the next marker a node. Simple terms, is that when we want to point to a stain, this point will bring us to another point no staining to staining.
Picture 4.png

#include<bits/stdc++.h>//每次扫描的元素被染色后指向其没有被染色的馒头 
#define N 1000010      //路径压缩以节省时间 
#define ll long long
using namespace std;
int n,m,p,q;
int pre[N],ans[N];
inline int Find(int x){
    return (pre[x]==x)? x:pre[x]=Find(pre[x]);
}
inline void Dye_mantou(ll l,ll r,int color)
{
    for(int i=Find(l);i<=r;i=Find(i)){          //方向指向数组末尾 
        ans[i]=color;
        pre[i]=i+1;
    }
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&p,&q);
    for(int i=1;i<=n+1;i++) pre[i]=i;
    for(int i=m;i>=1;i--){
        ll l=(i*p+q)%n+1;
        ll r=(i*q+p)%n+1;
        if(l>r) swap(l,r);
        Dye_mantou(l,r,i);
    }
    for(int i=1;i<=n;i++)
        printf("%d\n",ans[i]);
    return 0;
}

Example. 8: P2294 [HNOI2005] smart businessman

Weighted disjoint-set. By disjoint-set build relationships with the month of January.
Code:

#include<bits/stdc++.h>
#define N 100010
using namespace std;
int n,m,w;
int pre[N],d[N];
int Find(int x){
    if(pre[x]==x) return x;
    int temp=Find(pre[x]);
    d[x]+=d[pre[x]];
    return pre[x]=temp;
}
int Union(int x,int y,int v)
{
    int fx=Find(x);
    int fy=Find(y);
    if(fx==fy)
        return d[x]-d[y]==v;
    if(fx<fy){
        pre[fx]=fy;
        d[fx]=d[y]+v-d[x];
        return 1;
    } 
    else{
        pre[fy]=fx;
        d[fy]=d[x]-v-d[y];
        return 1;
    }
}
int main()
{
    scanf("%d",&w);
    while(w--)
    {
        scanf("%d%d",&n,&m);
        int flag=1;
        memset(d,0,sizeof(d));
        for(int i=0;i<=n;i++) pre[i]=i;
        for(int i=1,s,t,v;i<=m;i++){
            scanf("%d%d%d",&s,&t,&v);
            if(flag==0) continue;
            if(!Union(s-1,t,v)) flag=0;//包含s月,所以往前一个月为s-1
        }
        if(flag) printf("true\n");
        else printf("false\n");
    }
    return 0;
}

Example. 9: P1892 [BOI2003] groups

Two relationships subject has made it very clear, we will merge the relationship between the two friends. For two people the enemy relations, due to the enemy's enemy is my friend, so we can build a their own virtual enemy and then form friendships with each other.
Code:

#include<bits/stdc++.h>
#define N 6000
using namespace std;
int n,m,ans,pre[N];
char ch;
int Find(int x){
    return (x==pre[x])? x:pre[x]=Find(pre[x]);
}
inline void Union(int x,int y){
    int fx=Find(x);
    int fy=Find(y);
    pre[fx]=fy;
    return;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=2*n;i++) pre[i]=i;
    for(int i=1,u,v;i<=m;i++){
        cin>>ch>>u>>v;
        if(ch=='F') Union(u,v);
        if(ch=='E'){
            pre[Find(u+n)]=Find(v);
            pre[Find(v+n)]=Find(u);
        }
    }
    for(int i=1;i<=n;i++)
        if(pre[i]==i) ans++;
    printf("%d",ans);
    return 0;
}

  1. It does not matter how named

Guess you like

Origin www.cnblogs.com/cyanigence-oi/p/11774190.html