总结
这题太难,我太菜了,平时就写写2000一下,补个2400,要我老命,我太难了
const int N=3e5+5;
int fat[N*2],Size[N*2],n,k;///[1,n]使用,[n+1,2*n]不被使用
vector<int>vec[N];
int find(int x)
{
return fat[x]==x?x:fat[x]=find(fat[x]);
}
int calc(int x)///x或者y合法的最小连通块
{
int y=(x<=k?x+k:x-k);
x=find(x);
y=find(y);
if(x==0||y==0)///必定存在一个合法,一个不合法
return Size[x+y];
return min(Size[x],Size[y]);///取一个最合法的情况min
}
void merge(int x,int y)
{
x=find(x);
y=find(y);
if(y==0)
swap(x,y);
fat[y]=x;
if(x!=0)///合并,不存在不合法条件
Size[x]+=Size[y];
}
signed main()
{
IOS;
// file();
string str;
cin>>n>>k>>str;
for(int i=1;i<=k;i++)
fat[i]=i,fat[i+k]=i+k,Size[i]=1;
for(int i=1;i<=k;i++)
{
int tn;cin>>tn;
for(int j=0;j<tn;j++)
{
int pos;cin>>pos;
vec[pos].pb(i);
}
}
int ans=0;
for(int i=1;i<=n;i++)
{
if(vec[i].size()==1)
{
int x=vec[i][0];
ans-=calc(x);
if(str[i-1]=='1')///必不选置为0
fat[find(x)]=0;
else
fat[find(x+k)]=0;
ans+=calc(x);
}
else if(vec[i].size()==2)
{
int x=vec[i][0],y=vec[i][1];
if(str[i-1]=='1')
{
if(find(x)!=find(y))
{
ans-=calc(x);
ans-=calc(y);
merge(x,y);
merge(x+k,y+k);
ans+=calc(x);
}
}
else
{
if(find(x)!=find(y+k))
{
ans-=calc(x);
ans-=calc(y);
merge(x,y+k);
merge(x+k,y);
ans+=calc(x);
}
}
}
cout<<ans<<endl;
}
return 0;
}