Atcoder AGC004F : Namori

传送门

题解:

这种二分图反转相同颜色的第一反应就是先取个反。

然后我们考虑树的情况:
将树黑白染色,那么我们只能把颜色不同的相邻节点交换颜色。
其实这相当于把白点挪向黑点,然后原来的位置变为黑点。 显然白点和黑点的数量必须相等。
S i 表示 i 的子树中黑白点数量差,容易得到一个操作下界: | S i | ,这是子树内部与外部点配对的至少的数量。
同时这个下界又是可以达到的,我们尽可能让子树内部配对即可。

考虑环,我们分奇偶环讨论:
1.对于奇环,相当于我们可以新增加白点/黑点,而且这个新增的数量是一定的。我们先加上这个值并且在两边新增这么多个点,然后去掉这条边,就转化为树的情况。

2.对于偶环,相当于 x , y l c a ( x , y ) S i 要增加/减少 z ,列出式子发现要最小化: | A i z | + | B i + z | + | z | ,其实就是 A i B i z 的距离之和。 我们取其中的中位数做 z 最优。
时间复杂度 O ( n )

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

const int RLEN=1<<18|1;
inline char nc() {
    static char ibuf[RLEN],*ib,*ob;
    (ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
    return (ib==ob) ? -1 : *ib++;
}
inline int rd() {
    char ch=nc(); int i=0,f=1;
    while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
    while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
    return i*f;
}

const int N=1e5+50;
int n,m,stx,sty,dep[N],fa[N],s[N];
vector <int> edge[N]; LL ans=0;
inline void dfs(int x,int f,int val) {
    dep[x]=dep[f]+1; fa[x]=f; s[x]=val;
    for(auto v:edge[x]) {
        if(v!=f) {
            if(dep[v]) stx=x, sty=v;
            else dfs(v,x,-val), s[x]+=s[v];
        }
    }
}
inline int lca(int x,int y) {
    while(dep[x]>dep[y]) x=fa[x];
    while(dep[x]<dep[y]) y=fa[y];
    while(x!=y) x=fa[x], y=fa[y];
    return x;
}
int main() {
    n=rd(), m=rd();
    for(int i=1;i<=m;i++) {
        int x=rd(), y=rd();
        edge[x].push_back(y);
        edge[y].push_back(x);
    } dfs(1,0,1);
    if(m==n-1) {
        if(s[1]) {puts("-1"); return 0;}
        for(int i=1;i<=n;i++) ans+=abs(s[i]);
    } else {
        if((dep[stx]-dep[sty])&1) { // even circle
            if(s[1]) {puts("-1"); return 0;}
            static int a[N],tot;
            int l=lca(stx,sty);
            for(int u=stx;u!=l;u=fa[u]) a[++tot]=s[u],dep[u]=-1;
            for(int u=sty;u!=l;u=fa[u]) a[++tot]=-s[u],dep[u]=-1;
            a[++tot]=0; int mid=tot/2;
            nth_element(a+1,a+mid,a+tot+1);
            int z=a[mid];
            for(int i=1;i<=n;i++) if(~dep[i]) ans+=abs(s[i]);
            for(int i=1;i<=tot;i++) ans+=abs(a[i]-z);
        } else {
            if(s[1]&1) {puts("-1"); return 0;}
            int z=-s[1]/2; ans+=abs(z);
            for(int u=stx;u;u=fa[u]) s[u]+=z;
            for(int u=sty;u;u=fa[u]) s[u]+=z;
            for(int i=1;i<=n;i++) ans+=abs(s[i]);
        }
    } cout<<ans<<'\n';
}

猜你喜欢

转载自blog.csdn.net/qq_35649707/article/details/80679213
今日推荐