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;
}
posted @ 2021-06-01 19:33  nao-nao  阅读(72)  评论(0编辑  收藏  举报