Prefix Enlightenment CodeForces - 1291E(扩展域并查集)

There are n lamps on a line, numbered from 1 to n. Each one has an initial state off (0) or on (1).

You’re given k subsets A1,…,Ak of {1,2,…,n}, such that the intersection of any three subsets is empty. In other words, for all 1≤i1<i2<i3≤k, Ai1∩Ai2∩Ai3=∅.

In one operation, you can choose one of these k subsets and switch the state of all lamps in it. It is guaranteed that, with the given subsets, it’s possible to make all lamps be simultaneously on using this type of operation.

Let mi be the minimum number of operations you have to do in order to make the i first lamps be simultaneously on. Note that there is no condition upon the state of other lamps (between i+1 and n), they can be either off or on.

You have to compute mi for all 1≤i≤n.

Input
The first line contains two integers n and k (1≤n,k≤3⋅105).

The second line contains a binary string of length n, representing the initial state of each lamp (the lamp i is off if si=0, on if si=1).

The description of each one of the k subsets follows, in the following format:

The first line of the description contains a single integer c (1≤c≤n) — the number of elements in the subset.

The second line of the description contains c distinct integers x1,…,xc (1≤xi≤n) — the elements of the subset.

It is guaranteed that:

The intersection of any three subsets is empty;
It’s possible to make all lamps be simultaneously on using some operations.
Output
You must output n lines. The i-th line should contain a single integer mi — the minimum number of operations required to make the lamps 1 to i be simultaneously on.

Examples
Input
7 3
0011100
3
1 4 6
3
3 4 7
2
2 3
Output
1
2
3
3
3
3
3
Input
8 6
00110011
3
1 3 8
5
1 2 5 6 7
2
6 8
2
3 5
2
4 7
1
2
Output
1
1
1
1
1
1
4
4
Input
5 3
00011
3
1 2 3
1
4
3
3 4 5
Output
1
1
1
1
1
Input
19 5
1001001001100000110
2
2 3
2
5 6
2
8 9
5
12 13 14 15 16
1
19
Output
0
1
1
1
2
2
2
3
3
3
3
4
4
4
4
4
4
4
5
Note
In the first example:

For i=1, we can just apply one operation on A1, the final states will be 1010110;
For i=2, we can apply operations on A1 and A3, the final states will be 1100110;
For i≥3, we can apply operations on A1, A2 and A3, the final states will be 1111111.
In the second example:

For i≤6, we can just apply one operation on A2, the final states will be 11111101;
For i≥7, we can apply operations on A1,A3,A4,A6, the final states will be 11111111.
Sponsor

题意:
n个灯泡,每个灯泡为开起或者关闭。
k个序列,操作一次序列可以改变一部分灯泡的开关状态。
任意三个序列的交集为空集。
求使得前i (i ≤ n) 个灯泡开起的最小操作次数。

思路:

  1. 任意三个序列的交集为空集: 意味着任意一个灯泡至多被两个序列所控制。
  2. 这些序列的选择之间有明显的联系,可以想到图论算法
  3. 维护一个扩展域并查集,x代表选择x这个序列,x + k代表不选择这个序列。同时维护并查集的大小。
  4. 要是一个数被0个序列控制,不合理,不考虑
  5. 要是一个数被1个序列控制,那么如果这个s[i] = 0,令 f a [ f i n d ( x + k ) ] = 0 fa[find(x + k)] = 0 .。否则令 f a [ f i n d ( x ) ] = 0 fa[find(x)] = 0 。这里比较难懂的就是0这个节点代表什么。
  6. 0节点代表不能选这个节点,意味着合并的时候只能合并到0上面,并且不需要统计连通块大小
  7. 要是一个数被两个序列 x ,y 控制:
  8. 当s[i] = 1, 合并(x,y + k), (x + k,y),此时其实有两种选择,选x或者选x + k,选x就被动选择了y+k。选x + k就被动选择了y。
  9. 当s[i] = 0的时候合并(x,y), (x + k,y + k),同理也有两种选择。
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 1e6 + 7;
const int INF = 1e7;
char s[maxn];
int fa[maxn],siz[maxn];
int l[maxn][2];
int n,k;

int findset(int x)
{
    if(fa[x] == x)return x;
    return fa[x] = findset(fa[x]);
}

void Union(int x,int y)//y连x, 同时合并数目。
{
    int rx = findset(x),ry = findset(y);
    if(!ry)swap(rx,ry);
    if(rx) siz[rx] += siz[ry];
    fa[ry] = rx;
}

int cal(int x)//x和y实际是两种选择方案,任何一种选择方案都是合理的(除非为0),并且有其所配套(相连接)的方案,返回最小的一种即可
{
    int y = x + k;
    x = findset(x),y = findset(y);
    return min(siz[x],siz[y]);
}

int main()
{
    scanf("%d%d",&n,&k);
    scanf("%s",s + 1);
    for(int i = 1;i <= k;i++)
    {
        int m;scanf("%d",&m);
        for(int j = 1;j <= m;j++)
        {
            int x;scanf("%d",&x);
            if(l[x][0] == 0)l[x][0] = i;
            else l[x][1] = i;
        }
        siz[i] = 1;//i表示选择,i + k表示不选,所以siz[i]初始化为1.
        fa[i] = i;fa[i + k] = i + k;
    }
    siz[0] = INF;
    int ans = 0;
    
    for(int i = 1;i <= n;i++)
    {
        int x = l[i][0],y = l[i][1];
        int rx = findset(x),ry = findset(y);
        int rxk = findset(x + k),ryk = findset(y + k);

        if(!y)
        {
            ans -= cal(x);
            if(s[i] == '1') fa[rx] = 0;
            if(s[i] == '0') fa[rxk] = 0;
            ans += cal(x);
        }
        else if(s[i] == '0' && rx != ryk)
        {
            ans -= cal(x);
            ans -= cal(y);
            Union(x,y + k);Union(y,x + k);
            ans += cal(x);//x和y两个种类连起来了,所以加上一个就好了。
        }
        else if(s[i] == '1' && rx != ry)
        {
            ans -= cal(x);
            ans -= cal(y);
            Union(x,y);Union(x + k,y + k);
            ans += cal(x);
        }
        printf("%d\n",ans);
    }
    
    return 0;
}

发布了697 篇原创文章 · 获赞 22 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/tomjobs/article/details/104321167
今日推荐