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\n",ans);
    }
}

 

posted @ 2021-01-31 17:34  zlc0405  阅读(99)  评论(0编辑  收藏  举报