E. Prefix Enlightenment-(扩展域+边带权并查集)

总结

这题太难,我太菜了,平时就写写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;
}
发布了130 篇原创文章 · 获赞 5 · 访问量 4990

猜你喜欢

转载自blog.csdn.net/weixin_44224825/article/details/104271726
今日推荐