AGC004F Namori

Link

Part.1 树

众所周知树是个二分图。
因此我们可以先把树二分图染色,然后把二分图的一侧的点全部反色,那么我们可以把题目变成以下模型:
树上深度为奇数的点有一个球,每次我们可以把一个球移到它相邻的没有球的位置,然后我们要用最少的步数把所有初始的球和空位的位置全部交换。
显然有解的充要条件是初始球和空位数量相等。
对于每一条边,计算将其断开后剩下的两棵树内的球数的绝对值之差,那么这是这条边被操作次数的下界。
随便构造一下发现这个下界是可达到的。

Part.2 奇环套树

把环上的一条边((u,v))断开,那么剩下的就是一棵树,我们对剩下的这棵树二分图染色,那么(u,v)同色。
对于树的部分还是和Part.1一样先处理,然后((u,v))这条边进行一次操作相当于同时拿走两个点上的球或者同时在两个空位上放上球。
那么有解的充要条件就变成了球数与空位数的差值为偶数。
然后总的方案数就是这条边的操作数即总球数与总空位数的差值除以二,再加上进行这种操作之后按Part.1做的方案数。

Part.3 偶环套树

把环上的一条边((u,v))断开,那么剩下的就是一棵树,我们对剩下的这棵树二分图染色,那么(u,v)不同色。
显然有解的充要条件是初始球和空位数量相等。
假如我们在这条边操作了(x)次(在(u)(x)个球并在(v)(x)个球),那么这个操作对其它边的系数的影响会有一个(-1sim 1)的系数,把(0)的系数的边先按Part.1计算,剩下的可以看成数轴上取点该点到给定点集中的点的距离之和最短,显然中位数最优。

#include<cstdio>
#include<cctype>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<algorithm>
int read(){int x=0,c=getchar();while(!isdigit(c))c=getchar();while(isdigit(c))x=x*10+c-48,c=getchar();return x;}
void over(){puts("-1"),std::exit(0);}
const int N=500007;
std::vector<int>e[N];
int n,m,ans,eu,ev,a[N],fa[N],dep[N],f[N];
void dfs(int u)
{
    dep[u]=dep[fa[u]]+1,f[u]=dep[u]&1? 1:-1;
    for(int v:e[u]) if(v^fa[u]) dep[v]? (eu=u,ev=v):(fa[v]=u,dfs(v),f[u]+=f[v]);
}
void work1()
{
    if(f[1]) over();
    for(int i=1;i<=n;++i) ans+=abs(f[i]);
    printf("%d",ans);
}
void work2()
{
    if(f[1]&1) over();
    int x=-f[1]/2;
    for(;eu;eu=fa[eu]) f[eu]+=x;
    for(;ev;ev=fa[ev]) f[ev]+=x;
    for(int i=1;i<=n;++i) ans+=abs(f[i]);
    printf("%d",ans+abs(x));
}
void work3()
{
    if(f[1]) over();
    int c=0,x;
    for(;ev^eu;ev=fa[ev]) a[++c]=f[ev],dep[ev]=0;
    std::sort(a+1,a+c+1),x=a[(c+1)/2];
    for(int i=1;i<=n;++i) if(dep[i]) ans+=abs(f[i]);
    for(int i=1;i<=c;++i) ans+=abs(a[i]-x);
    printf("%d",ans+abs(x));
}
int main()
{
    n=read(),m=read();
    for(int i=1,u,v;i<=m;++i) u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
    dfs(1);
    m==n-1? work1():(abs(dep[eu]-dep[ev])&1? work3():work2());
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12283442.html