题目链接
题目解法
问题可以转述为如下形式:在一张会自行补边的有向图上不断加边,若 连向了 ,且 在一个二元环内, 也会连向 ,每次加入一条边,求出当前边数。
显然由二元环连接的点集中每一条可能的边都存在。
考虑将由二元环连接的点集缩点,则为了计算答案,需要维护指向该点集的入点集合
。
由此,一个点集
对答案的贡献即为
。
合并点集 时,考虑将 按照 的大小启发式合并,注意此处不是 的大小。即对于 ,将 中不在 中的元素插入 ,并删去 中在 中出现的元素。
为了判断加入一条边后是否需要合并点集,我们还需要额外维护 表示点集中所有入边和出边所在的点集的集合,合并时类似处理,可以保证总时间复杂度。
时间复杂度 。
#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;
}