zoukankan      html  css  js  c++  java
  • [可撤销并查集] Codeforces 1444C Team-Building

    题目大意

    给定一张 (n) 个点 (m) 条边的无向图,没有自环重边。
    每一个结点都在一个颜色的组中,共有 (k) 组,可能存在某组为空。
    求选出两组点,使它们能构成二分图的方案数。

    题解

    我们知道可以使用扩展域并查集来判二分图。即若存在边 ((u,v)),则把 (u)(v+n) 所在的集合合并,把 (u+n)(v) 所在的集合合并。若存在 (u)(u+n) 在同一集合中,则构成奇环,不是二分图。

    先把所有连接相同颜色的点的边加入并查集,分别判每种颜色的点构成的图是否是二分图。设有 (a) 种颜色的点自身无法构成二分图,那么还需考虑的颜色对数量为 (frac{1}{2}(n-a) imes(n-a-1))

    然后把所有连接不同颜色的点的边按两个点的颜色顺序排序,保证端点颜色相同的边相邻,一起处理。同时要忽略掉不能构成二分图的颜色。把每组端点颜色相同的边加入并查集,判这两个颜色的所有点能否构成二分图。若不能,答案减1。考虑完当前组边后,撤销当前组边合并的集合,然后考虑下一组边,所以需要使用可撤销并查集。时间复杂度 (O(mlog m+mlog n))

    Code

    #include <bits/stdc++.h>
    using namespace std;
    
    #define RG register int
    #define LL long long
    
    template<typename elemType>
    inline void Read(elemType &T){
        elemType X=0,w=0; char ch=0;
        while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
        while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
        T=(w?-X:X);
    }
    
    template<size_t N>
    struct UFS{
        int S[N],Rank[N];
        pair<int,int> stk[N*2];
        int top;
    
        void init(int n){
            top=0;
            for(int i=1;i<=n;++i)
                S[i]=i,Rank[i]=0;
        }
        int find(int u){
            while(u^S[u]) u=S[u];
            return u;
        }
        void merge_set(int u,int v){
            if((u=find(u))==(v=find(v))) return;
            if(Rank[u]<=Rank[v]){
                stk[++top]=make_pair(u,S[u]);
                S[u]=v;
                if(Rank[u]==Rank[v]){
                    stk[++top]=make_pair(-v,Rank[v]);
                    ++Rank[v];
                }
            }else{
                stk[++top]=make_pair(v,S[v]);
                S[v]=u;
            }
        }
        void undo(){
            int p=stk[top].first,u=stk[top].second;--top;
            if(p<0){
                Rank[-p]=u;
                p=stk[top].first,u=stk[top].second;--top;
                S[p]=u;
            }
            else S[p]=u;
        }
    };
    
    const int maxn=500010;
    UFS<maxn*2> S;
    vector<pair<int,int> > edge,data,banEdge;
    int belong[maxn];
    bool ban[maxn];
    int N,M,K,banNum=0;
    
    bool cmp(pair<int,int> A,pair<int,int> B){
        if(belong[A.first]==belong[B.first])
            return belong[A.second]<belong[B.second];
        return belong[A.first]<belong[B.first];
    }
    
    int main(){
        Read(N);Read(M);Read(K);
        S.init(N<<1);
        if(M==0){
            cout<<(LL)K*(K-1)/2<<endl;
            return 0;
        }
        S.init(N*2);
        for(int i=1;i<=N;++i)
            Read(belong[i]);
        int x,y;
        for(int i=1;i<=M;++i){
            int u,v;
            Read(u);Read(v);
            data.push_back(make_pair(u,v));
            if(belong[u]==belong[v]){
                S.merge_set(u,v+N);
                S.merge_set(u+N,v);
            }
        }
        int preTop=S.top;
        for(int u=1;u<=N;++u)
            if(S.find(u)==S.find(u+N))
                ban[belong[u]]=true;
        for(int i=1;i<=K;++i)
            if(ban[i]) ++banNum;
        for(auto e:data){
            int u=e.first,v=e.second;
            if(ban[belong[u]] || ban[belong[v]]) continue;
            if(belong[u]==belong[v]) continue;
            if(belong[u]>belong[v]) swap(u,v);
            edge.push_back(make_pair(u,v));
        }
        sort(edge.begin(),edge.end(),cmp);
        LL Ans=(LL)(K-banNum)*(LL)(K-banNum-1)>>1;
        int pre=0,pu=0,pv=0;
        for(int i=0;i<edge.size();++i){
            int u=edge[i].first,v=edge[i].second;
            if(belong[u]!=pu || belong[v]!=pv){
                while(S.top>preTop) S.undo();
                pre=i;pu=belong[u];pv=belong[v];
            }
            S.merge_set(u,v+N);
            S.merge_set(u+N,v);
            if(S.find(u)==S.find(u+N) || S.find(v)==S.find(v+N)){
                if(belong[u]>belong[v]) swap(u,v);
                banEdge.push_back(make_pair(belong[u],belong[v]));
            }
        }
        sort(banEdge.begin(),banEdge.end());
        banEdge.erase(unique(banEdge.begin(),banEdge.end()),banEdge.end());
        Ans-=banEdge.size();
        printf("%I64d
    ",Ans);
    
        return 0;
    }
    
  • 相关阅读:
    NET 连接池救生员(转载:M$)
    gitflow学习总结
    Apache Zookeeper
    ZZ: C++实现读取整行文本和每次只读入一个单词
    ZZ: C C++ string转char*
    memset
    prinf
    ZZ: C++ int转string from http://pppboy.blog.163.com/blog/static/302037962010378296766/
    XHTML下,JS浮动代码失效的问题 心境
    13个超酷的js显示时间效果 心境
  • 原文地址:https://www.cnblogs.com/AEMShana/p/14047449.html
Copyright © 2011-2022 走看看