【題解】CF603E Pastoral Oddities (分治)
考慮下這個奇怪的條件"每個點的度數都是奇數"。但這個圖是一個無向圖,點的總度數應該是偶數。因此對於任何一個聯通塊,這個聯通塊合法的必要條件是點的個數是偶數個,否則度數都不一樣。
然而這個同時是一個必要條件,這裏給出一個構造,你弄一顆dfs樹出來,不妨設這些點爲\(1\sim n\),\(1\)是根。按照dfs序反序考慮每個點,若一個點的度數是偶數則斷開父邊,否則保留這條父邊。通過這樣的算法我們可以直接欽定除了1的所有點的度數是奇數,由於斷開一條邊不改變總度數奇偶性(總度數變化量爲2),那麼1的度數\(\equiv 2-(n-1)\equiv n+1\pmod 2\)是奇數。
再考慮聯通塊之間的關係,發現兩個偶聯通塊連不連一起無所謂,不影響答案; 兩個奇聯通塊一定連一起這樣消掉了一個;一奇一偶連在一起也無所謂。因此有邊我們就連最優,而答案要求我們最大的邊最小,那麼這相當於做最小生成樹的過程。可以lct動態維護最小生成樹,也可以按時間分治。
考慮\(\rm Kruskal\)算法的過程,我們要從大到小枚舉每條邊,而且我們還要保證假如的邊的時間在前面。考慮這樣一種分治結構:
\({\rm solve}(l,r,L,R)\), 表示我要考慮\([l,r]\)時間中的答案,且這個答案範圍爲\([L,R]\),且並查集已經囊括了所有\(w<L\)且在時間\([l,r]\)中的邊。我們將\([l,\rm mid ]\)中的邊按邊權依次加入得到\(\rm ans[mid]\)。
此外我們發現\(\rm ans[]\) 是單調遞減的,因此我們可以確定\([l,\rm mid)\)的答案\(\ge \rm ans[mid]\),而\((mid,r]\)的答案\(\le \rm ans[mid]\)。也就是說:
對於\((mid,r]\)中的答案,所有\(> ans[\rm mid]\)的邊都可以忽略;
對於\([l,\rm mid )\)的答案,\(\le \rm ans[mid]\)的所有邊都會被加入並查集中。(這兩個結論都是根據最小生成樹的性質得到)
這也就意味着,一條邊在並查集中產生對\(\rm ans[x]\)的時間是一段連續的。
那麼在遞歸進入這兩邊子問題之前,把 可以忽略/必定加入 的邊先加入進來再遞歸。這樣一條邊至多會被\(O(\log n)\)次求解\(\rm ans[mid]\)的過程中被加入。(一條邊存在是一個區間,我們相當於把這個區間拆分成了\(O(\log n)\)個區間)
複雜度\(O(m\log m \log n )\)
//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
using namespace std; typedef long long ll;
inline int qr(){
int ret=0,f=0,c=getchar();
while(!isdigit(c)) f|=c==45,c=getchar();
while( isdigit(c)) ret=ret*10+c-48,c=getchar();
return f?-ret:ret;
}
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
int r[maxn],siz[maxn],stk[maxn],top,n,m,cnt_odd;
int Find(int x){return x==r[x]?x:Find(r[x]);}
int Merge(int x,int y){
x=Find(x),y=Find(y);
if(x==y) return 0;
if(siz[x]>siz[y]) swap(x,y);
cnt_odd-=siz[x]&1; cnt_odd-=siz[y]&1;
r[x]=y; siz[y]+=siz[x]; stk[++top]=x;
cnt_odd+=siz[y]&1;
return 1;
}
void Undo(int x=1e8){
while(x--){
int u=stk[top],v=r[u];
cnt_odd-=siz[v]&1;
siz[v]-=siz[u];
cnt_odd+=siz[v]&1; cnt_odd+=siz[u]&1;
r[u]=u;
top--;
}
}
int ans[maxn*3];
struct E{int u,v,w;}e[maxn*3];
map<int,vector<int>> C;
#define mid ((l+r)>>1)
void solve(int l,int r,int L,int R){
if(L>R) return;
int base=0,sav=0,sav1=0,ret=R;
for(int t=l;t<=mid;++t)
if(e[t].w<L) base+=Merge(e[t].u,e[t].v);
for(auto it=C.lower_bound(L),ed=C.upper_bound(R);it!=ed;++it)
for(auto t:it->second)
if(t<=mid){
if( cnt_odd) sav+=Merge(e[t].u,e[t].v);
if(!cnt_odd) {ret=e[t].w;it=prev(ed);break;}
}
ans[mid]=ret; Undo(sav);
if(mid<r) solve(mid+1,r,L,ret);
Undo(base);
for(auto it=C.lower_bound(L),ed=C.lower_bound(ret);it!=ed;++it)
for(auto t:it->second)
if(t<l) sav1+=Merge(e[t].u,e[t].v);
if(l<mid) solve(l,mid-1,ret,R);
Undo(sav1);
}
#undef mid
int main(){
memset(ans,0x3f,sizeof ans);
n=qr(),m=qr();
for(int t=1;t<=n;++t) r[t]=t,siz[t]=1;
cnt_odd=n;
for(int t=1;t<=m;++t) e[t].u=qr(),e[t].v=qr(),e[t].w=qr(),C[e[t].w].push_back(t);
solve(1,m,1,inf);
for(int t=1;t<=m;++t) printf("%d\n",ans[t]==inf?-1:ans[t]);
return 0;
}