1. 程式人生 > 實用技巧 >洛谷P6185 [NOI Online #1 提高組]序列(二分圖+並查集+思維)

洛谷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