Codeforces Round #616 C - Prefix Enlightenment(带权并查集维护二分图)

题意

给你 n n n个灯的开关状态,再给你 k k k个子集。选择一个子集将会按下子集中所有数字对应的开关。 m i m_i mi表示前 i i i个灯全部开启最少要选取多少个子集,求出所有的 m i ( i ∈ [ 1 , n ] ) m_i(i\in [1,n]) mi(i[1,n])
一些限制:

  1. 任意三个子集的交集都是空集。
  2. 一定存在一种选取方式使得所有的灯都亮着

n , k ≤ 3 e 5 n,k\le 3e5 n,k3e5

解题思路:

先观察限制条件:
条件1相当于一个灯最多被两个子集控制。那么如果它被两个子集控制,就在两个子集之间连一条边。如果原本灯的状态是0,那么两个子集必须2选1,如果原本状态是1,那么两个子集选取状态要相同。如果被1个子集控制,那么如果原本是0,那么这个子集必选,否则这个子集必不选。
那么就可以用并查集来维护点之间的关系,如果一个连通块内没有必选或者必不选某个点的限制,就取两种点中数目较少的点贡献答案,否则就取限制对应的点来贡献答案。条件2保证了并查集维护连通块的时候一定不会出现矛盾。

#include
#define ll long long
using namespace std;
const int maxn = 3e5 + 50;
char s[maxn];
int fa[maxn], f[maxn], must[maxn], sz[maxn][2];
vector<int> v[maxn];
int fnd(int x){
    if(x == fa[x]) return x;
    int t = fa[x];
    fa[x] = fnd(fa[x]);
    f[x] = f[t]^f[x];
    return fa[x];
}
int ans;
void link( int x, int y, int w){
    int rx = fnd(x), ry = fnd(y);
    if(rx == ry) return;
    if(must[rx] == -1) ans -= min(sz[rx][0], sz[rx][1]);
    else ans -= sz[rx][must[rx]];
    if(must[ry] == -1) ans -= min(sz[ry][0], sz[ry][1]);
    else ans -= sz[ry][must[ry]];
    f[ry] = f[x]^f[y]^w;
    fa[ry] = rx;
    sz[rx][0] += sz[ry][f[ry]];
    sz[rx][1] += sz[ry][f[ry]^1];
    if(must[rx] == -1 && must[ry] != -1) must[rx] = must[ry]^f[ry];
    if(must[rx] == -1) {
        ans += min(sz[rx][0], sz[rx][1]);
    }
    else ans += sz[rx][must[rx]];
    return;
}
int main()
{
    int n, k; cin>>n>>k;
    for(int i = 1; i <= k; ++i) fa[i] = i, must[i] = -1, f[i] = 0, sz[i][0] = 1;
    scanf("%s", s+1);
    for(int i = 1; i <= k; ++i){
        int c; scanf("%d", &c);
        while(c--){
            int x; scanf("%d", &x);
            v[x].push_back(i);
        }
    }
    ans = 0;
    for(int i = 1; i <= n; ++i){
        if(s[i] == '0'){
            if(v[i].size() == 1){
                int x = v[i][0];
                int rx = fnd(x);
                if(must[rx] == -1){
                    must[rx] = f[x];
                    ans -= min(sz[rx][0], sz[rx][1]);
                    ans += sz[rx][must[rx]];
                }
            }
            else {
                link(v[i][0], v[i][1], 1);
            }
        }
        else{
            if(v[i].size() == 2){
                link(v[i][0], v[i][1], 0);
            }else if(v[i].size() == 1){
                int x = v[i][0];
                int rx = fnd(x);
                if(must[rx] == -1){
                    must[rx] = f[x]^1;
                    ans -= min(sz[rx][0], sz[rx][1]);
                    ans += sz[rx][must[rx]];
                }
            }
        }
        printf("%d\n", ans);
    }
}


你可能感兴趣的:(数据结构,图论)