zoukankan      html  css  js  c++  java
  • 并查集

    把最近碰到的几个并查集题目综合一下。

    BZOJ-1854

    并查集来做,我们可以把一件装备看成一条边,两个属性看成两个点,那么这就相当于读入了一张图

    当读入每一个x,y时,我们找到两个点的祖先节点,fx,fy,我们保证祖先节点在该连通块

    中编号(装备属性)最大,用vis数组记录能否用属性I攻击boss,那么两种情况

    fx == fy,

      说明此时已经构成环了,环上的每个节点都可以被选。

    fx!=fy

      说明在两个联通分量。(设fx,fy为两个联通分量的父亲节点,假设fx<fy)

      两棵树,那只要把两棵树的父亲节点比较,最小的那个父亲节点可以被选定了,即pre[fx] = fy.

      两个环,两个环上的所有点都已经被选过了,新边不影响。

      一个环,一棵树,比较父亲节点的关系,来标记。别忘了把环的特性上传。

    最后扫描所有特性来确定。

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 1e6+5;
    typedef long long ll;
    int flag[maxn];
    int vis[maxn];
    int pre[maxn];
    void init()
    {
        memset(flag,0,sizeof(flag));
        memset(vis,0,sizeof(vis));
        for(int i=1;i<maxn;i++)
        {
            pre[i] = i;
        }
    }
    int find1(int x)
    {
        int r = pre[x];
        while(pre[r]!=r)
        {
            r = pre[r];
        }
        int i = x,j;
        while(pre[i]!=r)
        {
            j = pre[i];
            pre[i] = r;
            i = j;
        }
        return r;
    }
    void merge1(int x,int y)
    {
        int fx = find1(x);
        int fy = find1(y);
        if(fx==fy)
        {
            vis[fx] = 1;  ///标记是否可以用该点
            flag[fx] = 1; ///标记是否是环
        }
        else
        {
            if(flag[fx]==0&&flag[fy]==0)
            {
                ///将树上小的节点标记可用
                if(fx<fy) vis[fx] = 1,pre[fx] = fy;
                else vis[fy] = 1,pre[fy] = fx;
            }
            else if(flag[fx]==1&&flag[fy]==0)
            {
                if(fx>fy) vis[fy] = 1,pre[fy] = fx; ///fx为环,fy小,将fy连到fx上,同时标记fy
                else vis[fy] = 1,pre[fx] = fy,flag[fy] = 1;///fx为环,fy大,将fx连到fy上
                ///,同时标记fy并传递环特性
            }
            else if(flag[fy]==1&&flag[fx]==0)///fy为环
            {
                if(fx<fy) vis[fx] = 1,pre[fx] = fy; ///fx小,将fx连到fy上,同时标记fx
                else vis[fx] = 1,pre[fy] = fx,flag[fx] = 1;///fx大,将fy连到fx上,同时标记fx并传递环特性
            }
            else
            {
                if(fx>fy) pre[fy] = fx;
                else pre[fx] = fy;
            }
        }
    }
    int main()
    {
        int n;
        scanf("%d",&n);
        init();
        for(int i=1;i<=n;i++)
        {
            int x,y;
            scanf("%d %d",&x,&y);
            merge1(x,y);
        }
        for(int i=1;i<maxn;i++)
        {
            if(vis[i]) continue;
            else
            {
                cout<<(i-1)<<endl;
                return 0;
            }
        }
        return 0;
    }
    View Code

     E. Points, Lines and Ready-made Titles

    这道题和上面方法一样,我只多了个离散化。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 1e5+5;
    vector<int> x;
    vector<int> y;
    int xx[maxn],yy[maxn];
    int pre[maxn*2];
    int flag[maxn*2];
    int node[maxn*2];
    int find1(int x)
    {
        int r = x;
        while(pre[r]!=r)
        {
            r = pre[r];
        }
        int i = x,j;
        while(pre[i]!=r)
        {
            j = pre[i];
            pre[i] = r;
            i = j;
        }
        return r;
    }
    void merge1(int x,int y)
    {
        x = find1(x),y = find1(y);
        if(x==y)
        {
            flag[y] = 1; ///标记为环
        }
        else
        {
            if(flag[x]==0&&flag[y]==0) ///两个都是树
            {
                if(x<y) pre[x] = y,node[y] += node[x]; ///把节点个数算到y上
                else pre[y] = x,node[x] += node[y]; ///算到x上
            }
            else if(flag[x]==1&&flag[y]==0)///x是环
            {
                if(x<y) pre[x] = y,node[y] += node[x],flag[y] = 1;///y也标记为环
                else pre[y] = x,node[x] += node[y];
            }
            else if(flag[y]==1&&flag[x]==0)///y是环
            {
                if(y<x) pre[y] = x,node[x] += node[y],flag[x] = 1;///x也标记为环
                else pre[x] = y,node[y] += node[x];
            }
            else
            {
                if(y<x) pre[y] = x,node[x] += node[y];
                else pre[x] = y,node[y] += node[x];
            }
        }
    }
    const ll mod = 1e9+7;
    ll quick_pow(ll a,ll n)
    {
        ll ret = 1;
        while(n)
        {
            if(n%2LL) ret = ret*a%mod;
            n /= 2LL;
            a = a*a%mod;
        }
        return ret;
    }
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=0;i<maxn*2;i++) pre[i] = i;
        for(int i=0;i<maxn*2;i++) node[i] = 1,flag[i] = 0; ///每个连通分量初始值为1,环设为0
        for(int i=1;i<=n;i++)
        {
            scanf("%d %d",&xx[i],&yy[i]);
            x.push_back(xx[i]);
            y.push_back(yy[i]);
        }
        sort(x.begin(),x.end()); ///unique和sort连用
        x.erase(unique(x.begin(),x.end()),x.end());
        sort(y.begin(),y.end());
        y.erase(unique(y.begin(),y.end()),y.end());
        for(int i=1;i<=n;i++)
        {
            int tempx = lower_bound(x.begin(),x.end(),xx[i])-x.begin();
            int tempy = lower_bound(y.begin(),y.end(),yy[i])-y.begin();
            tempy = 1e5+tempy;
            merge1(tempx,tempy);
        }
        ll sum = 1;
        for(int i=0;i<maxn*2;i++)
        {
            if(pre[i]==i)
            {
                if(flag[i])
                    sum = sum*quick_pow(2,node[i])%mod;
                else sum = sum*(quick_pow(2,node[i])-1)%mod;
            }
        }
        printf("%I64d
    ",sum);
        return 0;
    }
    View Code

     jisuanke Artwork

    倒着并查集,代码复用的比较多。又臭又长。

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 1e3+10;
    int vis[maxn][maxn];
    int pre[maxn*maxn];
    struct node
    {
        int x1,y1,x2,y2;
    };
    node e[maxn*10];
    int n,m,q;
    int check(int x,int y)
    {
        if(x<=0||x>n||y<=0||y>m) return 0;
        else if(!vis[x][y])
        {
            return 1;
        }
        else return 0;
    }
    int cal(int i,int j)
    {
        return ((i-1)*m+j);
    }
    int find1(int x)
    {
        if(pre[x]==x) return x;
        else return pre[x] = find1(pre[x]);
    }
    int sum = 0;
    void merge1(int x,int y)
    {
        int fx = find1(x);
        int fy = find1(y);
        if(fx!=fy)
        {
            sum--;
            pre[fx] = fy;
        }
    }
    void fun(int i,int j)
    {
        if(!vis[i][j])
        {
            if(check(i-1,j)) merge1(cal(i,j),cal(i-1,j));
            if(check(i+1,j)) merge1(cal(i,j),cal(i+1,j));
            if(check(i,j-1)) merge1(cal(i,j),cal(i,j-1));
            if(check(i,j+1)) merge1(cal(i,j),cal(i,j+1));
        }
    }
    int tmp[maxn*10];
    int main()
    {
        scanf("%d %d %d",&n,&m,&q);
        memset(vis,0,sizeof(vis));
        sum = n*m;
        for(int i=1;i<=q;i++)
        {
            scanf("%d %d %d %d",&e[i].x1,&e[i].y1,&e[i].x2,&e[i].y2);
            int x1,x2,y1,y2;
            x1 = e[i].x1,x2 = e[i].x2;
            if(x1>x2) swap(x1,x2);
            y1 = e[i].y1,y2 = e[i].y2;
            if(y1>y2) swap(y1,y2);
            if(x1==x2)
            {
                for(int j=y1;j<=y2;j++)
                {
                    if(!vis[x1][j]) sum--;
                    vis[x1][j]++;
                }
            }
            else
            {
                for(int j=x1;j<=x2;j++)
                {
                    if(!vis[j][y1]) sum--;
                    vis[j][y1]++;
                }
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                pre[(i-1)*m+j] = (i-1)*m+j;
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                fun(i,j);
            }
        }
        tmp[q+1]= sum;
        for(int i=q;i>=1;i--)
        {
            int x1,x2,y1,y2;
            x1 = e[i].x1,x2 = e[i].x2;
            if(x1>x2) swap(x1,x2);
            y1 = e[i].y1,y2 = e[i].y2;
            if(y1>y2) swap(y1,y2);
            if(x1==x2)
            {
                for(int j=y1;j<=y2;j++) ///黑色框本来
                {
                    vis[x1][j]--;
                    if(check(x1,j)&&(pre[cal(x1,j)]==cal(x1,j)))
                    {
                         sum++;
                    }
                }
                for(int j=y1;j<=y2;j++)
                {
                    fun(x1,j);
                }
            }
            else
            {
                for(int j=x1;j<=x2;j++)
                {
                    vis[j][y1]--;
                    if(check(j,y1)&&(pre[cal(j,y1)]==cal(j,y1)))
                    {
                         sum++;
                    }
                }
                for(int j=x1;j<=x2;j++)
                {
                    fun(j,y1);
                }
            }
            tmp[i] = sum;
        }
        for(int i=2;i<=q+1;i++)
        {
            printf("%d
    ",tmp[i]);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    Final Zadanie 题解
    CF1096E The Top Scorer 题解
    [SDOI2008]Sue的小球 题解
    柱爷与远古法阵 题解
    [ZOJ3329] One Person Game 题解
    扑克牌 题解
    CF494C Helping People 题解
    CF1025D Recovering BST 题解
    linux基础学习-Raid 0 1 5 10的原理、特点、性能区别
    linux基础学习-CentOS7.5用户管理
  • 原文地址:https://www.cnblogs.com/littlepear/p/7696331.html
Copyright © 2011-2022 走看看