ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【YBT2022寒假Day8 B】【luogu CF603E】奇度边集 / Pastoral Oddities(结论)(cdq分治)(可撤回并查集)

2022-02-15 20:34:44  阅读:347  来源: 互联网

标签:sz Pastoral 可撤回 Day8 奇数 int mid 300001 sta


奇度边集 / Pastoral Oddities

题目链接:YBT2022寒假Day8 B / luogu CF603E

题目大意

给你一个 n 个点的图,然后一开始没有边,依次加边,然后每次问你当前是否存在一个边集,使得所有点度数都是奇数。
如果存在输出选的边权的最大边权的最小值,如果不存在输出 -1。

思路

首先我们要考虑存在点集所有点度数都是奇数的边需要怎样。
先说结论:需要不存在奇数个点的连通块。

首先证明奇数的肯定不行,不难看到那奇数个点,每个都是奇数个度数,那总度数就是奇数,但是一条边提供两个度数,总度数肯定是偶数,所以肯定不行。
然后证明偶数的行,我们可以对它做树形 DP,从根往上,连接没有连接的,已经连接的就不选它跟父亲的边,不难看出一定可以匹配完。

然后接着考虑怎么最小化边集的边权,不难想到一个类似最小生成树的算法。

然后这时候其实是可以用 LCT 来搞,就每次加边不断尝试删最小生成树中边权最大的边(用堆维护这些边),如果删到不合法了,那最后的边就是答案(也不能删了)。

但是这里我们也可以用 cdq 分治来做。
我们考虑把边按权值排序,然后我们设当前询问的区间(这里按插入时间排)为 \([l,r]\),答案区间为 \([L,R]\)(这里是按权值排序的顺序)。

然后就是对于一个位置 \(mid\) 求它的答案。
我们考虑用并查集维护,那首先我们要把 \([l,mid]\) 中在 \([1,L-1]\) 的边给放进去。

然后我们看看在 \([L,R]\) 中的边,如果它的位置在 \([1\sim mid]\) 就放进去,如果发现没有没有奇数连通块了,那就是当时的位置对于边的权值。
然后我们假设求出来的答案是 \(ans\)。

那显然我们要处理一些东西:
在处理 \([mid+1,r]\) 之前,我们要处理 \([l,mid]\) 中在 \([1,L-1]\) 的部分。
在处理 \([l,mid-1]\) 之前,我们要处理 \([L,ans]\) 权值之间的 \([1\sim l-1]\) 时间出现的给加入。

然后两边是分开的,所以我们要用的是可撤回并查集。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

struct node {
	int x, y, w, pl;
}a[300001], b[300001];
int n, m, pl[300001];
int ans[300001];

bool cmp(node x, node y) {
	return x.w < y.w;
}

struct Heap {
	int fa[300001], sz[300001], tot, sta[300001];
	
	void Init() {
		tot = n; for (int i = 1; i <= n; i++) fa[i] = i, sz[i] = 1;
	}
	
	int find(int now) {
		if (fa[now] == now) return now;
		return find(fa[now]);
	}
	
	void connect(int x, int y) {
		int X = find(x), Y = find(y);
		if (X == Y) return ;
		if (sz[X] > sz[Y]) swap(X, Y), swap(x, y);
		fa[X] = Y; tot -= (sz[X] & 1) + (sz[Y] & 1);
		sz[Y] += sz[X]; tot += (sz[Y] & 1);
		sta[++sta[0]] = X;
	}
	
	void back() {
		int X = sta[sta[0]], Y = fa[X];
		tot -= (sz[Y] & 1); sz[Y] -= sz[X];
		tot += (sz[X] & 1) + (sz[Y] & 1); fa[X] = X;
		sta[0]--;
	}
}H;

void clac(int mid, int l, int r, int L, int R) {
	int lst = H.sta[0];
	
	for (int i = l; i <= mid; i++)
		if (pl[i] < L) H.connect(b[i].x, b[i].y);
	for (int i = L; i <= R; i++) {
		if (a[i].pl <= mid) {
			H.connect(a[i].x, a[i].y);
			if (!H.tot) {
				ans[mid] = i; break;
			}
		}
	}
	
	while (H.sta[0] > lst) H.back();
}

void cdq(int l, int r, int L, int R) {
	if (l > r) return ;
	int mid = (l + r) >> 1;
	clac(mid, l, r, L, R);
	
	if (!ans[mid]) {
		for (int i = l; i <= mid; i++)
			if (pl[i] < L) H.connect(b[i].x, b[i].y);
		cdq(mid + 1, r, L, R);
		return ;
	}
	
	int lst = H.sta[0];
	for (int i = L; i <= ans[mid]; i++)
		if (a[i].pl < l) H.connect(a[i].x, a[i].y);
	cdq(l, mid - 1, ans[mid], R);
	while (H.sta[0] > lst) H.back();
	
	lst = H.sta[0];
	for (int i = l; i <= mid; i++)
		if (pl[i] < L) H.connect(b[i].x, b[i].y);
	cdq(mid + 1, r, L, ans[mid]);
	while (H.sta[0] > lst) H.back();
}

int main() {
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= m; i++) {
		scanf("%d %d %d", &a[i].x, &a[i].y, &a[i].w); a[i].pl = i;
	}
	memcpy(b, a, sizeof(b)); H.Init();
	sort(a + 1, a + m + 1, cmp);
	
	for (int i = 1; i <= m; i++)
		pl[a[i].pl] = i;
	cdq(1, m, 1, m);
	
	for (int i = 1; i <= m; i++)
		printf("%d\n", ans[i] ? a[ans[i]].w : -1);
	
	return 0;
}

标签:sz,Pastoral,可撤回,Day8,奇数,int,mid,300001,sta
来源: https://www.cnblogs.com/Sakura-TJH/p/YBT2022Day8_B.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有