LU ICPC Selection Contest 2020 and KFU Open Contest 2020

题目网址:https://codeforces.ml/gym/102862/problem/K
题意:
给我们一个0 1序列,这是目标序列,我们一开始只有全为0的初始序列,再给出m对操作(i,j),表示我们每次可以从这m对中选出一对(i,j),使得a[i]和a[j] 01反转,问是否可以从初始序列变成目标序列。

一开始不会,后来问了大佬才知道咋写,太妙了。
题解:
我们可以发现,每次操作,要是是0 0->1 1,要么 1 1->0 0,要么0 1->1 0,也就是说,0 1数量要么不变,要么是偶数倍的变,那么问题就简单了,我们只需将这m对建边,然后算出每个连通块的1的数量是不是偶数个就行了。这里我直接用并查集将这些点建立连接,然后维护1的数量。
代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int MAXN=1e5+5;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
int n;
int b[MAXN];
int m;
int l[MAXN];
int r[MAXN];
int fa[MAXN];
int sum[MAXN];
void init()
{
    
    
    for(int i=1;i<=n;i++) fa[i]=i;
}
int finder(int x)
{
    
    
    if(fa[x]==x) return x;
    else return fa[x]=finder(fa[x]);
}
void combine(int u,int v)
{
    
    
    int x=finder(u);
    int y=finder(v);
    if(x!=y)
    {
    
    
        fa[x]=y;
        b[y]+=b[x];
    }
}
int main()
{
    
    
   
    scanf("%d",&n);init();
    for(int i=1;i<=n;i++)
    {
    
    
        scanf("%d",&b[i]);
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
    
    
        scanf("%d%d",&l[i],&r[i]);
        combine(l[i],r[i]);
    }
    int flag=0;
    for(int i=1;i<=n;i++)
    {
    
    
        if(finder(i)==i)
        {
    
    
            if(b[i]&1)
            {
    
    
                flag=1;
                break;
            }
        }
    }
    if(flag) printf("No\n");
    else printf("Yes\n");
}

猜你喜欢

转载自blog.csdn.net/weixin_45755679/article/details/110209257
今日推荐