一盏灯最多只会在 2 2 2个集合里出现。
设包含第 i i i盏灯的两个集合依次为 L i , R i L_i,R_i Li,Ri(若没有则值为 0 0 0)。设 o p S = 1 op_S=1 opS=1表示集合 S S S选过, o p S = 0 op_S=0 opS=0表示没选过。
对于第 i i i盏灯的情况进行讨论
对于关联性问题,常常使用并查集来解决。
设 C o s t u Cost_u Costu表示改变改变集合 u u u状态的总代价, C a n t u Cant_u Cantu表示集合 u u u的状态是不是不能改变。
时间复杂度 O ( n α ( m ) ) O(n\alpha(m)) O(nα(m))
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5;
typedef int arr[N];
int n, m, Ans;
char s[N];
arr L, R, fa, tag, Cant, Cost, Size;
inline int Gf(int x) {
if (!fa[x])
return x;
if (!fa[fa[x]]) // 跳过根节点的tag
return fa[x];
int p = Gf(fa[x]);
tag[x] ^= tag[fa[x]];
return fa[x] = p;
}
inline void Merge(int a, int b) {
fa[a] = b,
Size[b] += Size[a],
Cost[b] += Cost[a],
Cant[b] |= Cant[a],
tag[a] ^= tag[b];
}
inline int op(int x) { return tag[x] ^ tag[fa[x]]; }
inline int Calc(int i) {
if (!L[i])
return Ans;
if (!R[i]) {
int x = Gf(L[i]);
Cant[x] = 1;
if (op(L[i]) == (s[i] - '0'))
Ans += Cost[x], tag[x] ^= 1, Cost[x] = -Cost[x];
return Ans;
}
int x = Gf(L[i]), y = Gf(R[i]);
if (Size[x] > Size[y])
swap(x, y);
if (x == y)
return Ans;
if ((op(L[i]) ^ op(R[i])) == s[i] - '0') {
int Cx = Cost[x], Cy = Cost[y];
if ((Cx < Cy && !Cant[x]) || Cant[y])
Ans += Cx, tag[x] ^= 1, Cost[x] = -Cx;
else
Ans += Cy, tag[y] ^= 1, Cost[y] = -Cy;
}
Merge(x, y);
return Ans;
}
int main() {
scanf("%d%d%s", &n, &m, s + 1);
for (int i = 1, c; i <= m; ++i) {
scanf("%d", &c);
for (int x; c--;)
scanf("%d", &x), L[x] ? R[x] = i : L[x] = i;
}
for (int i = 1; i <= m; ++i)
Size[i] = Cost[i] = 1;
for (int i = 1; i <= n; ++i)
printf("%d\n", Calc(i));
return 0;
}