洛谷題目傳送門c++
神仙思惟題仍是要寫點東西纔好。數組
每次操做把相鄰且同色的點反色,直接這樣思考會發現狀態有很強的後效性,沒辦法考慮轉移。spa
由於樹是二分圖,因此咱們轉化模型:在樹的奇數層的全部點上都有一枚棋子,每次能夠將棋子移向相鄰的空位,目標狀態是樹的偶數層的全部點上都有棋子。code
這樣的互換總次數有沒有一個下界呢?排序
咱們求出\(a_i\)表示點\(i\)子樹中棋子數量與空位數量之差(能夠是負數),那麼\(i\)的父邊就至少要交換\(|a_i|\)次。ip
爲何呢?子樹裏面空位比棋子少的話,確定要經過父邊把\(a_i\)個棋子送出去,才能移進來\(a_i\)個空位。反之亦然。get
因而,若是\(a_{rt}\)(根)不爲\(0\)就無解,不然\(\sum\limits_{i=1}^n|a_i|\)就是答案下界。it
而後,仔細推一下發現它就是答案。class
對於一個點,它的父邊和全部子邊的移進移出的順序,和其它點的順序是互相獨立的。im
等於說咱們總能安排一個合法的順序,使得它們一進一出一進一出。。。這樣完整地銜接起來,而不會互相矛盾。
顯然的想法:把整個環當作根,不在環上的點的答案照算不誤。因此把多出來一條邊去掉作好樹形DP。
首先這仍是一個二分圖,所以\(a_{rt}\)不爲\(0\)一樣無解。
因而,彷佛這條邊存在的意義只有分攤一部分轉移、減小總次數了。
那麼接下來要作的是,肯定環上每條邊的移動方向和次數。
設環長爲\(n\),\(A_i\)爲以\(i\)爲根求得的\(a\)值,\(x_i\)爲環上第\(i\)條邊的移動參數(正負表示方向,絕對值表示次數)
由於進出平衡,咱們能夠獲得一個方程組
\[\begin{cases}x_1-x_2=A_1\\x_2-x_3=A_2\\...\\x_n-x_1=A_n\end{cases}\]
這時候咱們發現上面那個方程組是沒有惟一解的。那到底該分攤多少呢?
不着急,咱們着眼於最小化\(\sum|x_i|\),再把方程組整一整
\[\begin{cases}x_1=x_1-0\\x_2=x_1-A_1\\x_3=x_1-A_1-A_2\\...\\x_n=x_1-\sum_{i=1}^{n-1}A_i\end{cases}\]
每一行的\(A\)都是前綴和的形式,在樹形DP的時候對應的是環上的每一個點的\(a\)。
看到這兒就豁然開朗了。這不等於說,數軸上有若干個點取值分別爲每個\(a\),找到一個點\(x_1\)使它到全部點距離之和最小?
把全部\(a\)排序,\(x_1\)不就能夠取第\(\frac n 2\)大的數到第\(\frac n 2+1\)大的數之間的任意整數麼?
最後把環上的DP值改一改就OK了。
由於不是二分圖了,因此直接解方程好像作不下去。咱們從更直觀的意義來理解它。
仍然是去掉一條邊作樹形DP。這時候咱們看看,在這條非樹邊上操做,在奇偶染色模型下等價於什麼呢?
沒錯,兩個棋子會在這裏同時變成空位,兩個空位會在這裏同時變成棋子!
那這條邊存在的意義,是把棋子或空位中多的減掉、少的補上。要操做多少次呢?\(\frac{\sum A_i}{2}\),在樹形DP中對應的是\(\frac{a_{rt}}{2}\)。
注意若是\(a_{rt}\)是奇數那麼無解。不然環底部的\(A\)發生了變化,致使要把環上的\(a\)都減掉\(\frac{a_{rt}}{2}\)。
最後的最後掃一遍數組統計答案。代碼沒有任何難點。
#include<bits/stdc++.h> #define R register int #define G if(++ip==ie)if(fread(ip=buf,1,SZ,stdin)) using namespace std; const int SZ=1<<19,N=1e5+9,M=2*N; char buf[SZ],*ie=buf+SZ,*ip=ie-1; inline int in(){ G;while(*ip<'-')G; R x=*ip&15;G; while(*ip>'-'){x*=10;x+=*ip&15;G;} return x; } int he[N],ne[M],to[M],f[N],a[N],b[N]; int Getf(R x){ return x==f[x]?x:f[x]=Getf(f[x]); } void Dfs(R x,R op){ a[x]=op; for(R y,i=he[x];i;i=ne[i]) if((y=to[i])!=f[x]) f[y]=x,Dfs(y,-op),a[x]+=a[y]; } int main(){ R n=in(),m=in(),rt=1,re=1,p=0,ans=0; for(R i=1;i<=n;++i)f[i]=i; for(R p=0,i=1;i<=m;++i){ R x=in(),y=in(); if(Getf(x)!=Getf(y))f[f[x]]=f[y]; else{rt=x,re=y;continue;}//把多的邊單獨拿出來 ne[++p]=he[x];to[he[x]=p]=y; ne[++p]=he[y];to[he[y]=p]=x; } f[rt]=0;Dfs(rt,1); if(n==m){//基環樹 for(R x=re;x;x=f[x])b[++p]=a[x]; if(p&1){//奇環 if(a[rt]&1)return puts("-1"),0; for(R x=re;x;x=f[x])a[x]-=a[rt]>>1; } else{//偶環 if(a[rt])return puts("-1"),0; sort(b+1,b+p+1); for(R x=re;x;x=f[x])a[x]-=b[p>>1]; } }//樹 else if(a[rt])return puts("-1"),0; for(R i=1;i<=n;++i)ans+=abs(a[i]); return cout<<ans<<endl,0; }