洛谷P6185 [NOI Online #1 提高组]序列(二分图+并查集+思维)
题目链接:https://www.luogu.com.cn/problem/P6185
为啥这题在洛谷难度标记只是个蓝题,我觉得明明有紫题的水准了吧,但这个题很不错,一开始的时候我没想出来转化成图论模型来做,就觉得这是一个很高大上的难题。但是看了别人写的题解之后,发现其实是一个非常不错的二分图带思维的题。
解法:
①问题相当于有一个序列ci=ai−bi,要进行若干次操作使得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"); } }