zoukankan      html  css  js  c++  java
  • 1290C. Prefix Enlightenment(带权并查集)

    一行上有n盏灯,编号从1到n。每个人的初始状态均为关闭(0)或打开(1)。

    您得到了{1,2,…,n}的k个子集A1,…,Ak,因此任何三个子集的交集都是空的。换句话说,对于所有1≤i1<i2 <i3≤k,Ai1∩Ai2∩Ai3=∅。

    在一项操作中,您可以选择这k个子集之一,并切换其中的所有灯的状态。对于给定的子集,可以确保使用这种类型的操作使所有的灯同时亮起。

    令mi为使第i个指示灯同时点亮所需的最少操作次数。请注意,其他灯(在i + 1和n之间)的状态没有任何条件,它们可以熄灭或点亮。

    您必须为所有1≤i≤n计算mi。

    题解:

    带权并查集的高级应用,说实话有点一知半解,很多2400分的题把基本知识点考察的很透彻。

    //问题可以转化为
    //每个集合拆成两个点,取点和不取点 
    //然后每个集合内的点,如果本来就是亮的,和不取点连边, 
    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=6e5+100;
    const int inf=1e9;
    int n,k,a[maxn];
    vector<int> g[maxn];
    string s;
    int father[maxn],w[maxn],ans;
    int findfather (int x) {
        int a=x;
        while (x!=father[x]) x=father[x];
        while (a!=father[a]) {
            int z=a;
            a=father[a];
            father[z]=x;
        }
        return x;
    }
    void Union (int x,int y) {
        x=findfather(x);
        y=findfather(y);
        if (x!=y) {
            father[x]=y;
            w[y]+=w[x]; 
        }
    }
    int dsu (int x) {
        return min(w[findfather(x)],w[findfather(x+k)]);//这个集合取或不取的最小值 
    }
    int main () {
        scanf("%d%d",&n,&k);
        cin>>s;
        for (int i=1;i<=k;i++) {
            int x;
            scanf("%d",&x);
            for (int j=0;j<x;j++) {
                int y;
                scanf("%d",&y);
                g[y].push_back(i);
            }
        }
        for (int i=1;i<=k*2;i++) father[i]=i,w[i]=(i>k);//不取为0,取为1 
        w[0]=inf;
        for (int i=1;i<=n;i++) {
            if (g[i].size()==1) {
                //如果这个点只在一个集合
                //x表示要使这个灯不亮的集合状态 
                int x=g[i][0]+(s[i-1]=='1')*k;
                ans-=dsu(g[i][0]);//答案减去这个集合取或不取的较小值 
                Union(x,0);//x和无限大的点连接,强制不取 
                ans+=dsu(g[i][0]);//答案加上这个集合取或不取的较小值 
            }
            else if (g[i].size()==2){
                int x=g[i][0];
                int y=g[i][1];
                if (s[i-1]=='0') {
                    if (findfather(x)!=findfather(y+k)) {
                        ans-=dsu(x)+dsu(y);
                        Union(x,y+k);
                        Union(x+k,y);
                        ans+=dsu(x);
                    }
                }
                else {
                    if (findfather(x)!=findfather(y)) {
                        ans-=dsu(x)+dsu(y);
                        Union(x,y);
                        Union(x+k,y+k);
                        ans+=dsu(x);
                    }
                }
            }
            printf("%d
    ",ans);
        }
    }
  • 相关阅读:
    SQL性能优化:如何定位网络性能问题
    ORACLE 10升级到10.2.0.5 Patch Set遇到的内核参数检测失败问题
    Linux 僵尸进程查杀
    Linux 虚拟机网络适配器从E1000改为VMXNET3
    v$session中server为none与shared值解析
    SQL SERVER导出特殊格式的平面文件
    XtraBackup出现 Can't connect to local MySQL server through socket '/tmp/mysql.sock'
    CentOS 6.6安装Xtrabackup RPM提示缺少libev.so.4()
    SQL Server Replication 中关于视图的点滴
    ORA-00988: missing or invalid password(s)
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/14353277.html
Copyright © 2011-2022 走看看