【LOJ3275】「JOISC 2020 Day2」有趣的 Joitter 交友

题目链接

点击打开链接

题目解法

问题可以转述为如下形式:在一张会自行补边的有向图上不断加边,若 x x 连向了 y y ,且 y , z y,z 在一个二元环内, x x 也会连向 z z ,每次加入一条边,求出当前边数。

显然由二元环连接的点集中每一条可能的边都存在。
考虑将由二元环连接的点集缩点,则为了计算答案,需要维护指向该点集的入点集合 i n e ine
由此,一个点集 S S 对答案的贡献即为 S × ( S 1 ) + S × i n e |S|\times (|S|-1)+|S|\times |ine|

合并点集 x , y x,y 时,考虑将 i n e ine 按照 S |S| 的大小启发式合并,注意此处不是 i n e |ine| 的大小。即对于 S x S y |S_x|\geq |S_y| ,将 i n e y ine_y 中不在 S x S_x 中的元素插入 i n e x ine_x ,并删去 i n e x ine_x 中在 S y S_y 中出现的元素。

为了判断加入一条边后是否需要合并点集,我们还需要额外维护 i n p , o u t p inp,outp 表示点集中所有入边和出边所在的点集的集合,合并时类似处理,可以保证总时间复杂度。

时间复杂度 O ( M L o g M L o g N ) O(MLogMLogN)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
int n, m, f[MAXN]; ll ans; vector <pair <int, int>> q;
set <int> ine[MAXN], points[MAXN], inp[MAXN], outp[MAXN];
int find(int x) {
	if (f[x] == x) return x;
	else return f[x] = find(f[x]);
}
ll cont(int x) {
	ll ans = 1ll * points[x].size() * (points[x].size() - 1);
	ans += 1ll * points[x].size() * ine[x].size();
	return ans;
}
void merge(int x, int y) {
	x = find(x), y = find(y); if (x == y) return;
	if (points[x].size() < points[y].size()) swap(x, y);
	f[y] = x, ans -= cont(x) + cont(y);
	for (auto v : points[y]) {
		points[x].insert(v);
		ine[x].erase(v);
	}
	for (auto v : ine[y])
		if (!points[x].count(v)) {
			ine[x].insert(v);
		}
	for (auto v : inp[y]) {
		if (v != x) {
			outp[v].erase(y);
			outp[v].insert(x);
		}
		if (!points[x].count(v)) {
			inp[x].insert(v);
			if (outp[x].count(v)) q.emplace_back(x, v);
		}
	}
	for (auto v : outp[y]) {
		if (v != x) {
			inp[v].erase(y);
			inp[v].insert(x);
		}
		if (!points[x].count(v)) {
			outp[x].insert(v);
			if (inp[x].count(v)) q.emplace_back(x, v);
		}
	}
	ans += cont(x);
}
void mergepoints() {
	while (!q.empty()) {
		int x = q.back().first, y = q.back().second;
		q.pop_back(), merge(x, y);
	}
}
int main() {
	read(n), read(m);
	for (int i = 1; i <= n; i++)
		f[i] = i, points[i].insert(i);
	for (int i = 1; i <= m; i++) {
		int x, y; read(x), read(y);
		if (find(x) != find(y) && inp[find(x)].count(find(y))) {
			q.emplace_back(x, y);
			mergepoints();
		} else if (find(x) != find(y) && !ine[find(y)].count(x)) {
			ine[find(y)].insert(x);
			inp[find(y)].insert(find(x));
			outp[find(x)].insert(find(y));
			ans += points[find(y)].size();
		}
		printf("%lld\n", ans);
	}
	return 0;
}
发布了813 篇原创文章 · 获赞 93 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/105074236