题解:P10683 [COTS 2024] 划分 Particija

· · 题解

先考虑原问题。

你考虑对每个点,两个划分中都有唯一一个包含它的,那么必须从二者中选出一个。可以建出二分图,对每个连通块取左右部点较小值即可。

考虑最小化答案。

我们发现我们需要分裂原连通块,而只要改的那一条边不是连接的两个孤点,存在方案将之连向一侧的连通块内部消除其影响,否则存在方案将其中一点加入某连通块较大一侧亦无影响,除非 n=1 特判即可。故只需找出所有割边,取其中对答案变化最大值即可。

考虑最大化答案。

我们发现我们需要合并原连通块,考虑如何选定最优连边对象,对一个左部点更少的块,我们会选定一个左部点更多的块与其相连,直接用左减右最大的块与其进行判定即可,反之同理。倘若选定的边是割边,先求出断边影响然后分别尝试为分出的两个块连边即可。

const int N=2e5+5,NN=N<<1;;

int n,nn;
int a[N],b[N];

namespace __0{
    int fa[NN<<1],sz[NN<<1];
    int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    void merge(int a,int b){a=find(a),b=find(b);if(a!=b)fa[a]=b,sz[b]+=sz[a];}
    bool same(int a,int b){return find(a)==find(b);}
    void solve(){
        nn=n<<1;
        rep(i,1,n)b[i]+=n;
        rep(i,1,nn<<1)sz[i]=i<=nn,fa[i]=i;
        rep(i,1,n)merge(a[i],b[i]+nn),merge(a[i]+nn,b[i]);
        int ans=0;
        rep(i,1,nn)if(fa[i]==i)ans+=min(sz[i],sz[i+nn]);
        put(ans);
    }
}

int ecnt,hd[NN],ne[NN],to[NN];
il void adde(int a,int b){ne[ecnt]=hd[a],to[ecnt]=b,hd[a]=ecnt++;}

int dfn[NN],low[NN],dcnt;
int scnt,scc[NN],stk[NN],top;
int sz1[NN],sz2[NN];
void tarjan(int x,int fe){
    dfn[x]=low[x]=++dcnt;
    stk[++top]=x;
    for(int e=hd[x];~e;e=ne[e])if(e!=(fe^1)){
        int y=to[e];
        if(!dfn[y])tarjan(y,e),chmin(low[x],low[y]);
        else chmin(low[x],dfn[y]);
    }
    if(low[x]>=dfn[x]){
        ++scnt;
        while(1){
            int u=stk[top--];
            scc[u]=scnt;
            if(u==x)break;
        }
    }
}

int bas,ans;
bool vis[NN];
vec<int> g[NN];

namespace __1{
    void dfs0(int x,int fa=0){
        vis[x]=1;
        for(auto y:g[x])if(y!=fa)dfs0(y,x),sz1[x]+=sz1[y],sz2[x]+=sz2[y];
    }
    void dfs(int x,int fa,int gr){
        for(auto y:g[x])if(y!=fa)
            dfs(y,x,gr),chmin(ans,-min(sz1[gr],sz2[gr])+min(sz1[y],sz2[y])+min(sz1[gr]-sz1[y],sz2[gr]-sz2[y]));
    }
    void solve(){
        if(n==1)return put(1);
        nn=n<<1;
        ecnt=0;rep(i,1,nn)hd[i]=-1;
        rep(i,1,n)adde(a[i],b[i]+n),adde(b[i]+n,a[i]);
        dcnt=scnt=0;rep(i,1,nn)dfn[i]=0;
        rep(i,1,nn)if(!dfn[i])tarjan(i,ecnt);
        rep(i,1,scnt)sz1[i]=sz2[i]=0;
        rep(i,1,nn)++(i<=n?sz1:sz2)[scc[i]];
        rep(i,1,scnt)g[i].clear(),vis[i]=0;
        rep(x,1,nn)for(int e=hd[x];~e;e=ne[e]){int y=to[e];if(scc[y]!=scc[x])g[scc[x]].pub(scc[y]);}
        ans=0,bas=0;
        rep(i,1,scnt)if(!vis[i])dfs0(i),bas+=min(sz1[i],sz2[i]),dfs(i,0,i);
        put(bas+ans);
    }
}

namespace __2{
    bool pd[NN];
    void dfs0(int x,int fa=0){
        vis[x]=1;
        for(auto y:g[x])if(y!=fa)dfs0(y,x),sz1[x]+=sz1[y],sz2[x]+=sz2[y],pd[x]|=pd[y];
    }
    set<pii> st;
    void work(int s1,int s2,int d=0,int tv=0){
        if(s1==s2)return;
        auto [v,y]=(s1<s2?*st.rbegin():*st.begin());
        if(y==d)tie(v,y)=(s1<s2?*++st.rbegin():*++st.begin());
        chmax(ans,-min(s1,s2)-min(sz1[y],sz2[y])+min(s1+sz1[y],s2+sz2[y])+tv);
    }
    void dfs(int x,int fa,int gr){
        for(auto y:g[x])if(y!=fa){
            int tv=-min(sz1[gr],sz2[gr])+min(sz1[y],sz2[y])+min(sz1[gr]-sz1[y],sz2[gr]-sz2[y]);
            dfs(y,x,gr),work(sz1[y],sz2[y],gr,tv),work(sz1[gr]-sz1[y],sz2[gr]-sz2[y],gr,tv);
        }
    }
    void solve(){
        if(n==1)return put(1);
        nn=n<<1;
        ecnt=0;rep(i,1,nn)hd[i]=-1;
        rep(i,1,n)adde(a[i],b[i]+n),adde(b[i]+n,a[i]);
        dcnt=scnt=0;rep(i,1,nn)dfn[i]=0;
        rep(i,1,nn)if(!dfn[i])tarjan(i,ecnt);
        rep(i,1,scnt)sz1[i]=sz2[i]=0;
        rep(i,1,nn)++(i<=n?sz1:sz2)[scc[i]];
        rep(i,1,scnt)g[i].clear(),vis[i]=0,pd[i]=0;
        rep(x,1,nn)for(int e=hd[x];~e;e=ne[e]){int y=to[e];if(scc[y]!=scc[x])g[scc[x]].pub(scc[y]);else pd[scc[x]]=1;}
        ans=0,bas=0;
        vec<int> rts;st.clear();
        rep(i,1,scnt)if(!vis[i])dfs0(i),bas+=min(sz1[i],sz2[i]),rts.pub(i),st.insert({sz1[i]-sz2[i],i});
        if(st.begin()->fir<=0&&st.rbegin()->fir<=0||st.begin()->fir>=0&&st.rbegin()->fir>=0)return put(bas);
        for(auto x:rts)if(sz1[x]!=sz2[x])
            if(pd[x])work(sz1[x],sz2[x]);
            else dfs(x,0,x);
        put(bas+ans);
    }
}

void INIT(){}
void MAIN(int K){
    read(n);
    rep(i,1,n)read(a[i]);
    rep(i,1,n)read(b[i]);
    if(K==0)return __0::solve();
    else if(K==1)return __1::solve();
    else return __2::solve();
}