Codeforces Round #616 (Div. 1) C.Prefix Enlightenment(種類並查集)

題目

給出一個長度爲n(n<=3e5)的01串S,

給出k(k<=3e5)個集合,每個集合裏是串裏的一些位置的下標,保證對於任意三個不同的集合i,j,k,A_{i} \cap A_{j} \cap A_{k} = \phi

你可以指定一個集合,將集合內的01全部取反,視爲依次操作

問,對於每個i,需要至少多少次操作,可以將前綴i個數都置1(此時不用關心後面的數是0還是1)

題目保證有解

思路來源

https://blog.csdn.net/wyy603/article/details/104156509

題解

考慮A_{i} \cap A_{j} \cap A_{k} = \phi,三個集合相交爲空,說明每個元素最多出現在兩個集合裏

沒出現,顯然不用管,

 

維護種類並查集,1到k是不選第i個集合,代價爲0,k+1到2k是選第i個集合,代價爲1

①出現在一個集合裏時,如果這一位本身爲1,就不選,否則必選

②出現在兩個集合裏時,不妨設x和y,如果爲1,同時選x和y,或同時不選;如果爲0,同時選x和y+k,或同時選x+k和y

對於一個固定大小的集合,選其或者其對立都是可行的方案,二者取小代價即可

對於②類型操作,考慮先減去上一次兩個獨立集合的貢獻,然後將其合併,再加上一個獨立集合的貢獻

對於①類型的操作,必選這個比較難討論,因爲要涉及到後續的集合合併

 

這裏的處理方式是,維護一個0節點,選0節點的代價爲INF,

將x和x+k兩個集合中一定不用的那個集合(不妨設爲x)和0節點合併,

選x的代價爲INF,就自然不會選INF了,達到了強制選x+k的目的

代碼

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10,M=2*N,INF=0x3f3f3f3f;
int n,k,c,x,y,par[M],cost[M],ans;
vector<int>a[N];//a[i][j]表示i受a[i][j]個集合控制
char s[N];
int find(int x){
    return par[x]==x?x:par[x]=find(par[x]);
}
void merge(int x,int y){
    x=find(x),y=find(y);
    if(x==y)return;
    par[x]=y;
    cost[y]+=cost[x];
}
int f(int x){
    return min(cost[find(x)],cost[find(x+k)]);
}
int main(){
    scanf("%d%d",&n,&k);
    scanf("%s",s+1);
    for(int i=1;i<=k;++i){
        scanf("%d",&c);
        for(int j=1;j<=c;++j){
            scanf("%d",&x);
            a[x].push_back(i);
        }
    }
    //拆點並查集 1-n爲不用 n+1 -2n爲用
    for(int i=1;i<=2*k;++i){
        par[i]=i;
        cost[i]= i>k;
    }
    par[0]=0;
    cost[0]=INF;
    for(int i=1;i<=n;++i){
        int sz=(int)a[i].size();
        if(sz==1){
            int x=a[i][0];
            int ban=(s[i]=='1')*k+x;
            ans-=f(x);
            merge(ban,0);
            ans+=f(x);
        }
        else if(sz==2){
            int x=a[i][0],y=a[i][1];
            if(s[i]=='1'){
                if(find(x)!=find(y)){
                    ans-=f(x)+f(y);
                    merge(x,y);
                    merge(x+k,y+k);
                    ans+=f(x);
                }
            }
            else{
                if(find(x)!=find(y+k)){
                    ans-=f(x)+f(y);
                    merge(x,y+k);
                    merge(x+k,y);
                    ans+=f(x);
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章