Codeforces Round #726 (Div. 2) F. Figure Fixing(二分图染色)

题意:

给出一个 n n n个点, m m m条边的无向连通图,每个点都有一个初始点权 v i v_i vi, 还有一个最终点权 t i t_i ti ,每次操作可以选一条边,然后将这条边上的两个点同时加k或减 k k k, k k k可以任意选择,求能否将 v i v_i vi变为 t i t_i ti

题解:

先将 t i − v i t_i-v_i tivi 当做每个点的点权。

首先,相邻点之间可以同时加 k k k, 可以推出相差偶数条边的两个点可以一个加 k k k,一个减 k k k 。假设1与2相连,2与3相连

那么 1 1 1 2 2 2 可以先同时加 k k k,然后 2 2 2 3 3 3再同时减 k k k ,最后就变成 v 1 + k , v 3 − k v_1+k,v_3-k v1+k,v3k

根据上述结论,可以推出以下两个情况:

1. 1. 1.图中没有奇环的时候,两个点满足上述结论,我们把相差偶数条边的点放在一起,那么可以分为两个集合,对集合内的点操作,点权总和不变,对两个集合的点操作,两个总和同时加k,若要同时变到 0 0 0,则说明两个集合的点权总和必须要相等。

2. 2. 2.若图中存在奇环,那么可以发现,其实任意两个点既可以同时加 k k k,又可以一个加 k k k另一个减 k k k ,那么问题就变成:一个数组,可以任意选取两个元素同时加或一加一减,求能否变到 0 0 0。很明显,一加一减操作并不会改变点权总和。那么同时加k要能变到 0 0 0,意味着点权总和必须为偶数,因为每次加了两个 k k k

还有最后一个难点,怎么求出相差偶数条边的点的集合,并且判断奇环呢?答案就是二分图染色,相差偶数条边的点的颜色一定相同,且能在染色过程中判断有无奇环。

代码:

#pragma GCC diagnostic error "-std=c++11"
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int> pii;
const int mod=1e9+7;
const int MAXN=2e5+5;
const int inf=0x3f3f3f3f;
int head[MAXN];
int cnt=0;
ll v[MAXN];
ll sum1=0,sum2=0;
int color[MAXN];
struct node
{
    int to;
    int next;
}e[MAXN<<1];
void add(int u,int v){
    e[cnt].to=v;
    e[cnt].next=head[u];
    head[u]=cnt++;
}
bool bfs()
{
    queue<int>q;
    color[1]=1;
    q.push(1);
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        if(color[now]==1) sum1+=v[now];
        else sum2+=v[now];
        for(int i=head[now];i!=-1;i=e[i].next)
        {
            int to=e[i].to;
            if(color[to]==-1)
            {
                color[to]=(color[now]^1);
                q.push(to);
            }
            else
            {
                if(color[to]==color[now])
                {
                    return false;
                }
            }
        }
    }
    return true;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            color[i]=-1;
            scanf("%lld",&v[i]);
        }
        ll sum=0;
        for(int i=1;i<=n;i++){
            ll x;
            scanf("%lld",&x);
            v[i]=x-v[i];
            sum=sum+v[i];
        }
        for(int i=1;i<=n;i++){
            head[i]=-1;
        }
        for(int i=1;i<=m;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            add(x,y);
            add(y,x);
        }
        sum1=sum2=0;
        bool flag=bfs();
        if(flag){
            if(sum1==sum2){
                printf("YES\n");
            }else{
                printf("NO\n");
            }
        }
        else{
            if(abs(sum)%2==0){
                printf("YES\n");
            }else{
                printf("NO\n");
            }
        }
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值