CF603E 整体二分
给定一张 \(n\) 个点的无向图,初始没有边。
依次加入 \(m\) 条带权的边,每次加入后询问是否存在一个边集,满足每个点的度数均为奇数。
若存在,则还需要最小化边集中的最大边权。
\(n \le 10^5\),\(m \le 3 \times 10^5\)。
显然答案是随着边数的增加而不下降的。
看每个点度数都是奇数意味着什么。可以发现他和无奇数大小的连通块互为充要条件。随便证明一下。
若连通块大小为奇数,那么意味着度数和也为奇数,而度数和必然为 \(2*m\) ,发生矛盾。
若联通块大小为偶数,随便弄一个生成树出来,可以构造出一个度数全为奇数的森林。
我们维护一个可撤销并查集,并且维护奇数连通块数量。容易发现,多连边不会使答案更劣。
整体二分,每次求出查询区间中间元素的答案,即可获得两边的答案范围。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int read()
{
int a = 0,x = 1;char ch = getchar();
while(ch > '9' || ch < '0') {if(ch == '-') x = -1;ch = getchar();}
while(ch >= '0' && ch <= '9') {a = a*10 + ch-'0';ch = getchar();}
return a*x;
}
int n,m;
namespace DSU{
const int N=1e6+7;
int fa[N],siz[N],top,sum;
struct Node{int a,fa;}stk[N];
void init(int n) {
for(int i = 1;i <= n;i ++) siz[i] = 1,fa[i] = i;sum = n;
}
int find(int s) {return fa[s] == s ? s : find(fa[s]);}
void merge(int a,int b)
{
a = find(a),b = find(b);
if(a == b) return ;
if(siz[a] > siz[b]) swap(a,b);
sum -= ((siz[a]&1)&&(siz[b]&1))*2;
fa[a] = b;siz[b] += siz[a];
stk[++top] = (Node){a,b};
// printf("add(%d,%d),sum = %d\n",a,b,sum);
}
void BACK()
{
if(!top) return ;
int a = stk[top].a,b = stk[top].fa;
fa[a] = a,siz[b] -= siz[a];-- top;
sum += ((siz[a]&1)&&(siz[b]&1))*2;
// printf("del(%d,%d),sum = %d\n",a,b,sum);
}
}
using namespace DSU;
int ans[N];
struct node{
int u,v,w,id;
}p[N],q[N];
bool cmp(node a,node b) {return a.w < b.w;}
void solve(int L,int R,int l,int r)
{
if(L>R) return ;
int MID = L+R>>1,cur = top;
for(int i = L;i <= MID;i ++) if(p[i].w <= l) merge(p[i].u,p[i].v);
int NOW=l;for(int i = l;i <= r && sum;i ++,NOW++) {
if(q[i].id <= MID) merge(q[i].u,q[i].v);//printf("NOW=%d ==\n",NOW);
}
NOW = max(NOW-1,l);//printf("NOW=%d\n",NOW);
if(!sum) ans[MID] = NOW;
else ans[MID] = -1;
while(top > cur) BACK();
for(int i = l;i <= NOW;i ++) if(q[i].id <= L) merge(q[i].u,q[i].v);
solve(L,MID-1,NOW,r);
while(top > cur) BACK();
for(int i = L;i <= MID;i ++) if(p[i].w <= l) merge(p[i].u,p[i].v);
solve(MID+1,R,l,NOW);
while(top > cur) BACK();
}
int main()
{
// freopen("random.in","r",stdin);
// freopen("sol.out","w",stdout);
n = read(),m = read();
for(int i = 1;i <= m;i ++) {
int u = read(),v = read(),w = read();
p[i] = q[i] = (node){u,v,w,i};
}
sort(q+1,q+1+m,cmp);init(n);
for(int i = 1;i <= m;i ++) p[q[i].id].v = i;
solve(1,m,1,m);
for(int i = 1;i <= m;i ++) printf("%d\n",ans[i]>0?q[ans[i]].w:-1);
return 0;
}