cf #616 (Div. 2) E. Prefix Enlightenment 拆點並查集

題目鏈接: http://codeforces.com/contest/1291/problem/E

題意:

你現在有一個 nn 位的 0101SS ,和 kk 個集合,每個集合裏會有 1,2,3,4,.....,n{1,2,3,4,.....,n} 中的若干個數字,並且保證每個數字只會在最多兩個集合中出現。

當你某次選擇某一個集合 xx 時,串 SS 中這個集合中的所有數字所在的位置都會翻轉( 01,100→1,1→0)。現在讓你找出將串中第 11 位至第 ii 位都翻轉爲 11 的最少選擇的集合數是多少,對 ii1n1-n 的答案均輸出,保證答案一定有解。

做法:

因爲 nn 的範圍很大所以只能使用 nlognnlog n 的做法,也是參考了別人的代碼後自己理解着寫的。

用的是所謂的拆點並查集的方法,我們先對於同一個位置 xx 來進行討論

如果它是 00
沒有集合擁有位置 xx 的情況不存在。
有一個集合 AA 擁有位置 xx 時,這個集合一定要被強制選擇。
有兩個集合 A,BA,B 擁有位置 xx 時,這兩個集合中一定要有一個要被選擇。

如果它是 11
沒有集合擁有位置 xx ,則可以不用看它。
有一個集合 AA 擁有位置 xx 時,這個集合一定要被強制不能選擇。
有兩個集合 A,BA,B 擁有位置 xx 時,這兩個集合要麼都要選,要麼都不選。

然後,我們可以將每個集合拆成兩個點,分別爲選,和不選。在上面的討論中,我們就可以將每次的情況進行處理,但是好像強制選擇或者不選擇比較難處理,所以加了一個超級點(即結果很大的一定不會被選擇的情況)。

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=600005;
int n,k,a[maxn],fa[maxn],val[maxn],big;
vector<int> ve[maxn];
int fin(int x){
    return fa[x]==x?x:fa[x]=fin(fa[x]);
}
int uni(int x,int y){
    int fx=fin(x),fy=fin(y);
    if(fx!=fy) fa[fx]=fy,val[fy]+=val[fx];
}
int gmin(int x){
    return min(val[fin(x)],val[fin(x+k)]);
}
int main() {
    scanf("%d%d",&n,&k);
    rep(i,1,n) scanf("%1d",&a[i]);
    rep(i,1,k){
        int x,y; scanf("%d",&x);
        while(x--) scanf("%d",&y),ve[y].push_back(i);
    }
    //1-n表示不取 n+1-2*n表示要取
    rep(i,1,2*k+1) fa[i]=i,val[i]=(i<=k);
    big=2*k+1;
    val[big]=(int)1e9;
    int ans=0;
    rep(i,1,n){
        if(ve[i].size()==1){
            // 如果a[i]爲0則強制要取該集合爲1
            int tmp=ve[i][0]+k*(a[i]==0);
            ans-=gmin(ve[i][0]);
            uni(tmp,big);
            ans+=gmin(ve[i][0]);
        }
        else if(ve[i].size()==2){
            int x=ve[i][0],y=ve[i][1];
            if(a[i]==0&&fin(x)!=fin(y+k)){
                ans=ans-gmin(x)-gmin(y);
                uni(x,y+k),uni(y,x+k);
                ans+=gmin(x);
            }
            if(a[i]==1&&fin(x)!=fin(y)){
                ans=ans-gmin(x)-gmin(y);
                uni(x,y),uni(x+k,y+k);
                ans+=gmin(x);
            }
        }
        printf("%d\n",ans);
    }

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