zoukankan      html  css  js  c++  java
  • hdu 4677 并查集合并(两个相邻区间并查集的合并)+分块算法 好题

    题意:点数n(n <= 30000), 边数(m <= 90000)的图,询问 q(1<=q<=30000)。

    对于每个询问(l, r),去掉(l,r)区间以外的所有点和其相关联的边,问剩下来的图的联通块的个数。

    思路:分块+并查集

    这题很容易想到分块, 难点是并查集的合并。

    对询问离线分块排序以后,我们对  左端点在相同块号内的询问 一起处理。这些询问的右断点是递增的,

    左端点在某个块内,我们对每个询问分成2个区域分别进行并查集处理,区域1:左端点所在的块的区域(并查集lp)

    (点的个数sqrt(n))。区域2:除区域1以外的其它询问点(并查集tf)。由于右断点是递增的,区域2很容易用一个并查集处理。区域1对于每个询问,都重新处理一遍并查集。最后关键就是两个并查集的合并,由于并查集不能进行删除操作,我们需要做的是   利用并查集tf里面的信息而不破坏它,并且复杂度要符合题意。我们可以在维护并查集lp的同时让这两个并查集合并,即在并查集f中把我当前需要的点搬到并查集lp里面,而没有必要把并查集tf全部搬过来,而且这样做复杂度也不符合要求,用一个vis数组就可以做到

    在实际处理的时候   并查集lp能处理到的点是整个询问的区域内的点。

    //#pragma comment(linker, "/STACK:102400000")
    #include<cstdlib>
    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #include<set>
    #include<map>
    #include<list>
    #include<queue>
    #include<vector>
    #define tree int o,int l,int r
    #define lson o<<1,l,mid
    #define rson o<<1|1,mid+1,r
    #define lo o<<1
    #define ro o<<1|1
    #define pb push_back
    #define mp make_pair
    #define ULL unsigned long long
    #define LL long long
    #define inf 0x7fffffff
    #define eps 1e-7
    #define N 30009
    /*
    知识:
    1.两个并查集的合并:用vis数组标记元素是否已经合并了!(复杂度与find函数一致,很小)
    */
    using namespace std;
    int m,n,T,t,bit,ns,tp;
    struct node
    {
        int l,r,id,b;
        bool operator<(const node a)const
        {
            return b<a.b||(b==a.b&&r<a.r);
        }
    } q[N];
    vector<int>g[N];
    int ans[N],tf[N],lf[N],vis[N];
    void init()
    {
        for(int i=0; i<=n; i++)
        {
            g[i].clear();
        }
        memset(vis,-1,sizeof(vis));
    }
    void initset(int f[],int l,int r)
    {
        while(l<=r)
        {
            f[l]=l;
            l++;
        }
    }
    int find(int f[],int x)
    {
        return f[x]==x?x:f[x]=find(f,f[x]);
    }
    int unionone(int f[],int u,int v)
    {
        int tu=find(f,u);
        int tv=find(f,v);
        f[tu]=tv;
        return tu!=tv;
    }
    int findtwe(int x,int k)
    {
        if(vis[x]!=k)
        {
            lf[x]=tf[x];
            vis[x]=k;
        }
        return lf[x]==x?x:lf[x]=findtwe(lf[x],k);
    }
    int uniontwe(int u,int v,int k)
    {
        int tu=findtwe(u,k);
        int tv=findtwe(v,k);
        lf[tu]=tv;
        return tu!=tv;
    }
    int fenkuai(int k,int &last)
    {
        tp+=q[k].r-last+1;//必须记住上一次得到的集合的个数,WA了多次
        initset(tf,last,q[k].r);
        for(int i=last; i<=q[k].r; i++)
        {
            for(int j=0; j<g[i].size(); j++)
            {
                int v=g[i][j];
                if(v>=(q[k].b*bit+1)&&v<=q[k].r)
                        tp-=unionone(tf,i,v);
            }
        }
    
        int ans=tp+q[k].b*bit-q[k].l+1;/////////////WA
        initset(lf,q[k].l,q[k].b*bit);//q[k].b*bit不会大于n
        for(int i=q[k].l; i<=q[k].b*bit; i++)vis[i]=k;
        for(int i=q[k].l; i<=q[k].b*bit; i++)
        {
            for(int j=0; j<g[i].size(); j++)
            {
                int v=g[i][j];
                if(v>=q[k].l&&v<=q[k].r)
                    ans-=uniontwe(i,v,k);
            }
        }
        last=q[k].r+1;
        return ans;
    }
    int bfans(int l,int r)
    {
        int ans=r-l+1;
        initset(lf,l,r);
        for(int i=l+1; i<=r; i++)
        {
            for(int j=0; j<g[i].size(); j++)
            {
                int v=g[i][j];
                if(v>=l&&v<=r)
                    ans-=unionone(lf,i,v);
            }
        }
        return ans;
    }
    
    void solve()
    {
        for(int i=0; i<m;)
        {
            int b=q[i].b,last=(q[i].b)*bit+1;
            tp=0;
            while(i<m&&q[i].b==b)
            {
                if(q[i].r<(q[i].b)*bit+1)
                {
                    ans[q[i].id]=bfans(q[i].l,q[i].r);
                }
                else
                {
                    ans[q[i].id]=fenkuai(i,last);
                }
                i++;
            }
        }
    }
    int main()
    {
    #ifndef ONLINE_JUDGE
        freopen("ex.in","r",stdin);
    #endif
        int ncase=0;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d",&n,&m);
            init();
            for (int i=1; i<=m; ++i )
            {
                int x,y;
                scanf("%d%d",&x,&y);
                g[x].push_back(y);
                g[y].push_back(x);
            }
            bit=sqrt(n+0.5);
            ns=(n+bit-1)/bit;
            scanf("%d",&m);
            for(int i=0; i<m; ++i)
            {
                scanf("%d%d",&q[i].l,&q[i].r);
                q[i].id=i;q[i].b=(q[i].l+bit-1)/bit;
            }
            sort(q,q+m);
            solve();
            printf("Case #%d:
    ",++ncase);
            for(int i=0; i<m; ++i)
                printf("%d
    ",ans[i]);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    快速了解MongoDB
    如何在 Ubuntu 上安装 MongoDB
    Python操作MongoDB文档数据库
    C# Convert.ToDouble 在不同的区域下的问题
    Visual Studio 调试C#程序时,提示“无法加载程序集****.XmlSerializers.dll",文件找不到 的问题
    C# 调用打印机打印文件
    C# 获取所有可用的打印机
    记录一次排查使用HttpWebRequest发送请求的发生“基础连接已关闭:接收时发生错误”异常问题的过程
    SQL 分组后只获取每组的一条数据
    记录一次使用NPOI遇到的问题
  • 原文地址:https://www.cnblogs.com/sbaof/p/3346267.html
Copyright © 2011-2022 走看看