【Codeforces 1290】C Prefix Enlightenment(带权并查集)
被抓来看题
然后还没看完就切掉了把我喊去看
结果还没看完就地震了
首先任意三个集合交为空
那么任意一个元素最多出现在两个集合内
考虑把集合看做点,元素看做边
那么实际上实际上就是给图黑白染色
每个边要求两边同/异色
用带权并查集维护同异色个数即可
对于只出现在一个集合的元素特殊处理即可
#include<bits/stdc++.h>
using namespace std;
#define re register
#define cs const
#define pb push_back
#define pii pair<int,int>
#define fi first
#define se second
#define ll long long
#define bg begin
cs int RLEN=1<<20|1;
inline char gc(){
static char ibuf[RLEN],*ib,*ob;
(ib==ob)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
return (ib==ob)?EOF:*ib++;
}
inline int read(){
char ch=gc();
int res=0;bool f=1;
while(!isdigit(ch))f^=ch=='-',ch=gc();
while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
return f?res:-res;
}
inline void readstring(char *s){
int top=0;char ch=gc();
while(isspace(ch))ch=gc();
while(!isspace(ch)&&ch!=EOF)s[++top]=ch,ch=gc();
}
template<class tp>inline void chemx(tp &a,tp b){a<b?a=b:0;}
template<class tp>inline void chemn(tp &a,tp b){a>b?a=b:0;}
cs int N=600005;
int n,k;
ll ans;
int fa[N],kd[N];
ll same[N],dif[N];
vector<int> lnk[N];
inline pii find(int x){
if(x==fa[x])return pii(x,0);
pii now=find(fa[x]);
fa[x]=now.fi,kd[x]^=now.se;
return pii(fa[x],kd[x]);
}
inline void merge(int u,int v,int tp){
pii v1=find(u),v2=find(v);tp^=v1.se,tp^=v2.se;
int f1=v1.fi,f2=v2.fi;
if(f1==f2)return;
ans-=min(same[f1],dif[f1]),ans-=min(same[f2],dif[f2]);
if(tp)same[f1]+=dif[f2],dif[f1]+=same[f2],kd[f2]=1;
else same[f1]+=same[f2],dif[f1]+=dif[f2],kd[f2]=0;
ans+=min(same[f1],dif[f1]),fa[f2]=f1;
}
inline void calc(int u,int tp){
pii x=find(u);
ans-=min(same[x.fi],dif[x.fi]);
if(tp^x.se)dif[x.fi]+=1e9;
else same[x.fi]+=1e9;
ans+=min(same[x.fi],dif[x.fi]);
}
char a[N];
signed main(){
#ifdef Stargazer
freopen("lx.in","r",stdin);
#endif
n=read(),k=read();
readstring(a);
for(int i=1;i<=k;i++){
fa[i]=i,same[i]=1,dif[i]=0,kd[i]=0;
int m=read();
while(m--)lnk[read()].pb(i);
}
for(int i=1;i<=n;i++){
if(lnk[i].size()==1) calc(lnk[i][0],a[i]=='0');
if(lnk[i].size()==2) merge(lnk[i][0],lnk[i][1],a[i]=='0');
cout<<ans<<'\n';
}
}