abc434e

https://atcoder.jp/contests/abc434/tasks/abc434_e
这道题如果我们考虑将 \(x - r\)\(x + r\) 连边,它肯定会形成一堆联通块。
我们看这个联通块的形状,如果是一棵树,因为我们的任务是给每一条边都选一个相邻的点,所以此时我们最大就是可以选边的数量个点。
比如说下面这个:
image
其中橙色的点是我们选的点,箭头代表每一条边对应所选的点。
如果不是一棵树,我们可以选出它任意一个生成树,这样答案就是点数减去 \(1\),然后我们再考虑其他的边中的任意一个边,显然我们可以从这条边所连的点中再选取一个,这样的话最大就是联通块中点的数量。
image
就像这样,其中绿色的是我们选的那个生成树,橙色的那条边是我们另选出来的那条边,紫色的点是我们生成树中选的点,紫色的箭头表示我们生成树中的边对应所选的点,橙色的点是我们另选出来的那条边所选的点。


然后我们发现,答案其实就是总的点数减去树的个数。
实现的话我们可以使用并查集来维护(记得对点离散化)。

点击查看代码
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>

using std::cin;
using std::cout;
const int N = 2e5 + 10;

int tot;
int go[N << 1];
int a[N << 1];
int sizep[N << 1];
int sizee[N << 1];
std::vector<int> vec;
std::map<int, int> map;

int find(int x)
{
	if (x == go[x])
		return x;
	return go[x] = find(go[x]);
}

int main()
{
	int n;
	cin >> n;
	for (int i = 1; i <= n; ++i)
	{
		int x, r;
		cin >> x >> r;
		a[++tot] = x + r;
		a[++tot] = x - r;
		vec.push_back(x - r);
		vec.push_back(x + r);
	}
	for (int i = 1; i <= 2 * n; ++i)
		go[i] = i, sizep[i] = 1;
	std::sort(vec.begin(), vec.end());
	vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
	int all = vec.size();
	for (int i = 1; i <= tot; ++i)
		map[a[i]] = std::lower_bound(vec.begin(), vec.end(), a[i]) - vec.begin() + 1;
	for (int i = 2; i <= tot; i += 2)
	{
		int u = find(map[a[i - 1]]);
		int v = find(map[a[i]]);
		if (u == v)
			sizee[u]++;
		else
		{
			go[u] = v;
			sizee[v] += sizee[u] + 1;
			sizep[v] += sizep[u];
		}
	}
	int ans = all;
	for (int i = 1; i <= all; ++i)
	{
		if (find(i) == i)
			ans -= (sizee[i] == sizep[i] - 1);
	}
	cout << ans << '\n';
	return 0;
}
posted @ 2025-11-30 20:16  SigmaToT  阅读(24)  评论(0)    收藏  举报