【题解】[JOISC2020] ジョイッターで友だちをつくろう
关于我读错题调了一个小时这档事,这个关注操作不具有传递性,也就是说如果 \(x\) 关注 \(y\) ,\(x\) 所在强连通分量的人不用关注 \(y\) 。。。
题意比较简单,转化一下,给定 \(N\) 个点,支持加边,维护强连通分量,每个分量的贡献为 \(size^2-size\) ,对于缩点后每条边的贡献为终点所在强连通分量的 \(size\) 。再次强调只计算终点的 \(size\) ,笔者因为看成两端 \(size\) 乘积调了一年。
转化完后你会发现,这这题怎么这么熟悉,**[WC2021] 括号路径 **是这道题的弱化版,只用维护 \(size^2\) 即可。
所以我们开 set
维护分别维护每一个强连通分量中的点,连向当前分量的其他分量,当前分量出发到达的其他分量,和到达当前分量且不在当前分量中的点。
合并的时候可能会引起多个后续合并,所以我们再用队列维护一下需要合并的点对即可。
时间复杂度 \(\mathcal{O}(M\log M\log N)\) 。这道题做的人还是少,做过这题参加 WC2021 可收获巨大 BUFF。
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 100005
using namespace std;
int n, m, col[N];
long long ans;
set<int>s[N], in[N], out[N], ed[N];
queue<pair<int, int>>q;
typedef set<int>::iterator ite;
long long g(int x) {
return 1LL * s[x].size() * (s[x].size() - 1 + ed[x].size());
}
void merge(int u, int v) {
q.push(make_pair(u, v));
while (!q.empty()) {
int x = col[q.front().first], y = col[q.front().second];
q.pop();
//cout<<"ss "<<x<<" "<<y<<endl;
if (x == y)
continue;
if (s[x].size() > s[y].size())
swap(x, y);
ans -= g(x) + g(y);
if (in[x].find(y) != in[x].end())
in[x].erase(y), out[y].erase(x);
if (in[y].find(x) != in[y].end())
in[y].erase(x), out[x].erase(y);
for (ite it = s[x].begin(); it != s[x].end(); it++) {
col[*it] = y, s[y].insert(*it);
ite cur = ed[y].find(*it);
if (cur != ed[y].end())
ed[y].erase(cur);
}
for (ite it = in[x].begin(); it != in[x].end(); it++) {
out[*it].erase(x);
out[*it].insert(y);
if (out[y].find(*it) != out[y].end())
q.push(make_pair(*it, y));
else
in[y].insert(*it);
}
for (ite it = out[x].begin(); it != out[x].end(); it++) {
in[*it].erase(x);
in[*it].insert(y);
if (in[y].find(*it) != in[y].end())
q.push(make_pair(*it, y));
else
out[y].insert(*it);
}
for (ite it = ed[x].begin(); it != ed[x].end(); it++)
if (col[*it] != y)
ed[y].insert(*it);
ans += g(y);
}
}
void ins(int x, int y) {
int w = x;
x = col[x];
y = col[y];
if (x == y)
return;
if (in[x].find(y) != in[x].end())
merge(x, y);
else in[y].insert(x), out[x].insert(y), ans -= g(y) , ed[y].insert(w), ans += g(y);
}
int main() {
scanf("%d%d", &n, &m);
rep(i, 1, n)col[i] = i, s[i].insert(i);
rep(i, 1, m) {
int x, y;
scanf("%d%d", &x, &y);
ins(x, y);
printf("%lld\n", ans);
}
return 0;
}