洛谷P6185 [NOI Online #1 提高组]序列(二分图+并查集+思维)

题目链接:https://www.luogu.com.cn/problem/P6185

为啥这题在洛谷难度标记只是个蓝题,我觉得明明有紫题的水准了吧,但这个题很不错,一开始的时候我没想出来转化成图论模型来做,就觉得这是一个很高大上的难题。但是看了别人写的题解之后,发现其实是一个非常不错的二分图带思维的题。

解法:

①问题相当于有一个序列ci=aibi,要进行若干次操作使得ci每一项都为0

②对于操作2 u v,我们在u和v之间连接一个无向边,形成一个联通块,把联通块通过并查集标记该连通块序号。我们发现在这个操作之中,每个u和v之间的数值都可以互相转化,但是仔细一想会发现这个规律,该连通块内数值的和恒不变。那OK,我们就把这个连通块缩点,该点的权值为该连通块内所有节点的和。

③处理完操作2了,我们就要处理操作1 u v了。通过之前操作,我们把u=id[u],v=id[v]。操作1是生死与共,即两边u和v同时±1,那我们继续想着把u和v连边建立新的无向图,然后想想操作1,就是无向图内所有节点总和±2对吧。那我们回归到①这一块,要让每个ci都为0,那么显然所有节点和就是0了。所以判断有无最终解的一个前提是所有节点的sum是不是为偶数,如果不是偶数那就直接掰掰;然后我们考虑是不是二分图。因为如果是二分图的话,其实就是把所有节点分成了2部分,因为我之前提过是同生共死的关系,所以左部分和与右部分和之间的差值恒不变。而每一个部分之间又可以内部转化,所以呢可想而知如果左部分和!=右部分和,那么ci的和永远不可能为0,所以另一个判断条件在二分图的情况下,左部分和与右部分和相等;接下来想不是二分图的情况了,之前说过内部转化(本质其实就是2点之间距离为偶数的点之间可以通过中间点架桥来实现类似于操作2的一边+一边-的情况),既然不是二分图就无所谓什么左部分右部分了,直接来呗,所有点都可以转化嘛。那么我们想把n-1个点全部转化成0,把sum集中到一个点上(之前说过前提是sum为偶数),具体例子你们自己想,很容易想出。我提供一个: ①-②,②-③,③-①,①=100,②=③=0。那么就是把①变成50,②变成-50;①再变成0,③再变成-50;②和③同时变为0,那就转化完了。懂了吧?所以对于不是二分图的情况来说,只要sum为偶数就行.

AC代码:

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,n,a) for(int i=n;i>=a;i--)
#define endl '\n'
#define eps 0.000000001
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a))
#define IO ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
const int INF=0x3f3f3f3f;
const ll inf=0x3f3f3f3f3f3f3f3f;
const int mod=1e9+7;
const int maxn=1e5+5;
int n,m,a[maxn],b[maxn];
int p[maxn],q[maxn],id[maxn],fa[maxn],cnt;
ll val[maxn],sum,sum1,sum2;
vector<int> G[maxn];int col[maxn],flag;
int find(int x){
    while(x!=fa[x]) x=fa[x]=fa[fa[x]];
    return x;
}
void dfs(int x){
    if(col[x]==1)sum1+=val[x];
    else sum2+=val[x];
    sum+=val[x];
    for(auto v:G[x]){
        if(col[v]){
            if(col[v]==col[x]) flag=0;
        }else{
            col[v]=3-col[x];
            dfs(v);
        }
    }
}
int main(){
    int T;scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        rep(i,1,n) scanf("%d",&a[i]),fa[i]=i;
        rep(i,1,n) scanf("%d",&b[i]),a[i]-=b[i];
        int ct=0;
        rep(i,1,m){
            int o,a,b;scanf("%d%d%d",&o,&a,&b);
            if(o==1){p[++ct]=a;q[ct]=b;}
            else{
                int u=find(a),v=find(b);
                if(u!=v) fa[u]=v;
            }
        }
        cnt=0;
        rep(i,1,n){
            if(find(i)==i) id[i]=++cnt;
        }
        rep(i,1,cnt){
            G[i].clear();col[i]=0;val[i]=0;
        }
        rep(i,1,n){
            val[id[find(i)]]+=a[i];
        }
        rep(i,1,ct){
            int u=id[find(p[i])],v=id[find(q[i])];
            G[u].pb(v);G[v].pb(u);
        }
        bool yes=1;
        rep(i,1,cnt){
            if(!col[i]){
                flag=1;sum=sum1=sum2=0;
                col[i]=1;
                dfs(i);
                if(sum%2!=0){
                    yes=0;break;
                }
                if(flag&&sum1!=sum2){
                    yes=0;break;
                }
            }
        }
        if(yes)puts("YES");
        else puts("NO");
    }
}
View Code

 

posted @ 2020-07-21 13:39  Anonytt  阅读(184)  评论(0编辑  收藏  举报