zoukankan      html  css  js  c++  java
  • [带权扩展域并查集] Codeforces 1290C Prefix Enlightenment

    好题,收藏下。

    题目大意

    给定一个长度为 (n) 的 01 串 (S),和 (k)(S) 的下标子集,且任意三个子集的交集为空集。一次操作可以选择一个子集,将子集中的下标对应的 (S_i)取反。令(m_i)为让(S_{1 sim i}=1) 的最少操作次数,求出所有的(m_i),保证有方案。

    (1leq n,k leq 3 imes 10^5)

    题解

    任意三个子集的交集为空集,显然,一个点最多只能在两个集合中出现,这样所有集合的size之和是(O(n))的。

    一个在两个集合中出现的点(i)相当于连接了2个集合(u_i)(v_i),这是一个图。
    不妨把整张图染成选和不选两种颜色。
    (S_i=0),则(i)的状态要改变,可以发现,(u_i)(v_i)一定异色。
    (S_i=1),则(i)的状态不变,可以发现,(u_i)(v_i)一定同色。

    接下来就用扩展域并查集来做,一个颜色的点的权值是(0),另一个颜色的点的权值是(1),取并查集合并后两个集合的权值的最小值即为答案。

    如果(i)在任何一个给出的集合中都未出现过,则我们无法去修改(S_i),但因为题目保证有解,所以不用去管。
    但若某个(i)只在一个集合中出现,怎么去强制选点?领略到了一个神奇的做法,可以建一个点权为无穷大的虚点,把该点和虚点连接。

    Code

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    #include <vector>
    using namespace std;
    
    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);
    }
    
    vector<int> G[300010];
    char Str[300010];
    int S[600010],Rank[600010],Value[600010];
    int N,K,Ans=0;
    
    int Find(int u){
        if(S[u]==0) return u;
        return S[u]=Find(S[u]);
    }
    
    void union_set(int u,int v){
        if((u=Find(u))==(v=Find(v))) return;
        else if(Rank[u]>Rank[v]) {S[v]=u;Value[u]+=Value[v];}
        else if(Rank[u]<Rank[v]) {S[u]=v;Value[v]+=Value[u];}
        else {S[v]=u;Rank[u]++;Value[u]+=Value[v];}
    }
    
    inline int Val(int u){
        return min(Value[Find(u)],Value[Find(u+K)]);
    }
    
    int main(){
        Read(N);Read(K);
        scanf("%s",Str+1);
        for(register int i=1;i<=K;++i){
            Value[i+K]=1;
            int Len;Read(Len);
            for(register int j=1;j<=Len;++j){
                int x;Read(x);
                G[x].push_back(i);
            }
        }
        Value[K*2+1]=0x3f3f3f3f;
        for(register int i=1;i<=N;++i){
            if(G[i].size()==1){
                int u=G[i][0];
                if(Str[i]=='1') u+=K;
                Ans-=Val(G[i][0]);
                union_set(u,K*2+1);
                Ans+=Val(G[i][0]);
            }
            else if(G[i].size()==2){
                int u=G[i][0],v=G[i][1];
                if(Str[i]=='0' && Find(u)!=Find(v+K)){
                    Ans-=Val(u)+Val(v);
                    union_set(u,v+K);union_set(u+K,v);
                    Ans+=Val(u);
                }
                else if(Str[i]=='1' && Find(u)!=Find(v)){
                    Ans-=Val(u)+Val(v);
                    union_set(u,v);union_set(u+K,v+K);
                    Ans+=Val(u);
                }
            }
            printf("%d
    ",Ans);
        }
        return 0;
    }
    
  • 相关阅读:
    (转载)直接用SQL语句把DBF导入SQLServer
    (转载)SQLServer存储过程返回值总结
    (转载)MS SQL Server 未公开的加密函数有哪些?
    (转载)SQL语句,纵列转横列
    (转载)直接用SQL语句把DBF导入SQLServer
    (转载)用SQL语句创建Access表
    (转载)根据数据字典表定义的表结构,生成创建表的SQL语句
    (转载)sql语句解决分页问题
    (转载)总结一下SQL语句中引号(')、quotedstr()、('')、format()在SQL语句中的用法
    (转载)异构数据库之间完全可以用SQL语句导数据
  • 原文地址:https://www.cnblogs.com/AEMShana/p/12334717.html
Copyright © 2011-2022 走看看