2017国家集训队作业[agc004f]Namori

2017国家集训队作业[agc004f]Namori

题意:

给你一颗树或环套树,树上有\(N\)个点,有\(M\)条边。一开始,树上的点都是白色,一次操作可以选择一条端点颜色相同的边,使它的端点颜色同时取反,即白色变成黑色或黑色变成白色。问,最少需要几次操作才可以把整棵树都涂成黑色?(\(N\leq10^5,N-1\le M\le N\)

题解:

参考:https://blog.csdn.net/werkeytom_ftd/article/details/78393489

我们把深度为奇数的点看做一个洞,深度为偶数的点看做有一个球,一次操作等于是把一颗球移动到相邻的空位里。那么本题就等价于把每一个球都移动到单独的一个洞里。(这就想不到了。。。)

然后这题有三种情况:

树:

根据上面的构造,树可以被看成一个二分图,那么存在可行方案当且仅当球数等于硬币数。

不妨记洞的权值为\(1\),球的权值为\(-1\),记\(v_i\)为点的权值,\(s_i\)为子树内所有权值的和。

那么最小答案就是\(\sum_{i=1}^n |s_i|\)。意义就是以\(i\)为根的子树内至少需要移进\(s_i\)个球或移出\(-s_i\)球。

奇环套树:

肯定想到的是把环上的某一条边断开,是图变成树。由于是个奇环,那么断开边上的两个端点的在新树上的深度奇偶性一定相等。那么树上的结论依然适用于这种情况。

考虑有什么不同的地方。我们发现,如果我们要操作的一开始断开的边,根据题目要求,操作之前,这两个点的颜色必须一致,我们操作这条边就等价于把这条边上的两个端点同时拿走一个球或同时放入一个球。也就是说,可行的情况变宽了,原来必须要球数和洞数相等,现在只需要球数和洞数的奇偶相等就可以。

因为让球数与洞数相等的最小操作数为\(C= \sum_{i=1}^n v_i\)

那么答案的最小可能值为\(C+\sum_{i=1}^n |s_i|\)。事实上,我们确实可以构造证明出这就是答案的最小值。(有时间再补上证明)这种情况就解决了。

偶环套树:

与奇环套树不同的是,偶环套树中环上的边断开后形成的树中,断开的边的两个端点在新树上的深度奇偶性不一样。我们没法想上一种情况一样扩宽可行条件。

但这一条边使得我们有多一种操作让球入洞,使答案变小。

假设我们在这条边上操作了\(X\)次,\(X\)为任意整数。假设这条边的两端分别为\(vs,vt\),钦定\(X\),使\(vs\) 那一端移入\(X\)个球或移出\(-X\)个球,\(vt\)那端移出\(X\)个球或移入\(-X\)个球。假设我们一开始就进行这个操作,那么这个操作只会影响包含且仅只包含\(vs\)的子树或包含且仅只包含\(vt\)的子树的权值。然后就可以用树的方法进行操作了。

记每个点的子树受\(X\)的影响为\(k_i\in\{-1,0,1\}\),最后答案就是\(|X|+\sum_{i=1}^n|k_iX+s_i|\)的最小值,取中位数即可。

呼~~~

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fo(i,l,r) for(int i=l;i<=r;i++)
#define of(i,l,r) for(int i=l;i>=r;i--)
#define fe(i,u) for(int i=head[u];i;i=e[i].next)
using namespace std;
typedef long long ll;
inline int rd()
{
    static int x,f;
    x=0,f=1;
    char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}
const int N=100010;
struct edge{
    int v,next;
    edge(int v=0,int next=0):v(v),next(next){}
}e[N<<1];
int n,m,s[N],k[N],dep[N],q[N],vs,vt,tt;
int tot=0,head[N];
bool vis[N],flag;
 
inline void add(int u,int v){e[++tot]=edge(v,head[u]);head[u]=tot;}
inline int fabs(int a){return a<0?-a:a;}
 
void _dfs(int u,int fat)
{
    vis[u]=1;
    fe(i,u){
        int v=e[i].v;
        if(v==fat)continue;
        if(vis[v])vs=u,vt=v,flag=(dep[v]==dep[u]);
        else dep[v]=dep[u]^1,_dfs(v,u);
    }
}
 
inline bool cmp(int u,int v){return (u==vs&&v==vt)||(u==vt&&v==vs);}
void dfs(int u,int fat)
{
    fe(i,u){
        int v=e[i].v;
        if(v==fat)continue;
        if(cmp(u,v))continue;
        dfs(v,u);
        s[u]+=s[v];k[u]+=k[v];
    }
}
 
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("inc.txt","r",stdin);
    #endif
    n=rd();m=rd();
    fo(i,1,m){
        int x=rd(),y=rd();
        add(x,y);add(y,x);
    }
    dep[1]=1;flag=0;
    _dfs(1,0);
    int delta=0;ll ans=0;
    fo(i,1,n)delta+=(s[i]=dep[i]?1:-1);
    if(m==n-1){if(delta){puts("-1");return 0;}}
    else{
        if(flag){
            if(delta&1){puts("-1");return 0;}
            s[vs]-=(delta>>1),s[vt]-=(delta>>1);
            ans+=abs(delta>>1);
        }
        else{
            if(delta){puts("-1");return 0;}
            k[vs]=1;k[vt]=-1;
        }
    }
    dfs(1,0);
    q[tt=1]=0;
    fo(i,1,n){
        if(!k[i])ans+=fabs(s[i]);
        else q[++tt]=-s[i];
    }
    sort(q+1,q+tt+1);
    int med=q[(tt+1)>>1];
    fo(i,1,tt)ans+=fabs(q[i]-med);
    printf("%lld\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/JackyhhJuRuo/p/9507811.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值