Codeforces 1290 C Prefix Enlightenment —— 带权并查集,有丶东西

This way

题意:

给你一个长度为n的01串,m个几何,每个集合包含1-n的一些数,并且
在这里插入图片描述
对于第i个集合,如果你选择了它,那么01串中对应的集合里面的位置的数就会取反。
问你到第i个位置时,前面i个位置都是1,后面的位置随意,问你最少需要选择多少个集合。

题解:

我猜到是并查集,但是只能想到对于第i个集合有取和不取两种情况,之后对于权值怎么考虑就不会了。
有点难讲,主要还是靠自己意会。首先我们发现任意三个集合的交集为0,也就是说对于任意一个位置,它最多只会被包含在两个集合中。那么我们分情况考虑。
对于这个位置有0个集合包含它的,不做处理。
有一个集合的,那么如果这个位置是0,就意味着这个集合一定要取,也就是说没有不取这个集合的选项,由于我们答案要做到最少,那么将不取这个选项和一个无限大的点并查集一下即可。如果它是1,那么就将取这个选项和无限大并一下。但是由于这个集合我们之前可能用到过,并且使用的是它取和不取两种值中的最小值,那么我们一开始要减一下。
之后是两个集合的情况,那么如果这个位置上的值是0,也就是只能取一个集合,那么我们将c[i][0]和c[i][1]+m放到一个集合中,将另两个放到一个集合中。同理,我们需要减去这两个集合之前的影响。
对于是1的情况,和上面类似。

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
int mi[N*2];
int c[N][2];
char s[N];
int fa[N*2];
int finds(int x){return x==fa[x]?fa[x]:fa[x]=finds(fa[x]);}
void merge(int x,int y){
    int fax=finds(x);
    int fay=finds(y);
    if(fax!=fay)
        fa[fay]=fax,mi[fax]+=mi[fay];
}
int main()
{
    int n,m;
    scanf("%d%d%s",&n,&m,s+1);
    for(int i=1;i<=m;i++){
        int x,a;
        scanf("%d",&x);
        while(x--)
            scanf("%d",&a),c[a][c[a][0]>0]=i;
    }
    for(int i=1;i<=m*2;i++)
        fa[i]=i,mi[i]=i<=m;
    fa[m*2+1]=m*2+1;
    mi[m*2+1]=1e9;
    int ans=0;
    for(int i=1;i<=n;i++){
        if(c[i][0]&&!c[i][1]){
            if(s[i]=='0'){//必须取,那么将不取放入n*2+1的并查集中,意味着以后不会有不取这个选项
                ans-=min(mi[finds(c[i][0])],mi[finds(c[i][0]+m)]);
                //因为不知道前面的运算中,这个是取还是不取,但是一定是用最小的那个
                merge(2*m+1,c[i][0]+m);
                ans+=mi[finds(c[i][0])];
            }
            else{//同上类似,但是这次将“取”这个选项放入不可能并查集中
                ans-=min(mi[finds(c[i][0])],mi[finds(c[i][0]+m)]);
                merge(2*m+1,c[i][0]);
                ans+=mi[finds(c[i][0]+m)];
            }
        }
        else if(c[i][0]&&c[i][1]){
            int x=c[i][0],y=c[i][1];
            if(s[i]=='0'){//两个中取一个
                if(finds(x)!=finds(y+m)){
                    ans-=min(mi[finds(x)],mi[finds(x+m)])+min(mi[finds(y+m)],mi[finds(y)]);
                    merge(x,y+m);
                    merge(x+m,y);
                    ans+=min(mi[finds(x)],mi[finds(y)]);
                }
            }
            else{
                if(finds(x)!=finds(y)){
                    ans-=min(mi[finds(x)],mi[finds(x+m)])+min(mi[finds(y+m)],mi[finds(y)]);
                    merge(x,y);
                    merge(x+m,y+m);
                    ans+=min(mi[finds(x)],mi[finds(x+m)]);
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

发布了584 篇原创文章 · 获赞 33 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/tianyizhicheng/article/details/104651361