题目
思路来源
ak区up主&jiangly代码
题解
能理解大概怎么回事但是不会写代码,抄一下jiangly代码
首先,转成二分图,左侧每行一个点1到点n,右侧每列一个点1到点m,
考虑颜色,B 的话就 u->v,R 的话就 v->u,然后求强连通分量scc,
scc内每个点有两个被指向的方向,
换言之就是无论以什么顺序topo都会成环,所以必须同时选中然后钦定颜色
而钦定大小为k的时候,k=1的时候代价为0,k>1的时候是k^2,
所以,总代价是不为1的scc的大小的平方和
然后询问是两个点没有相同的,所以说明都是加边
然后强行把动态图做成离线的,
独立地看每一条边,需要考虑的是这两条边两端点,是在第几次操作后首次在同一个scc里的
所以分治,对于当前边集,把[l,mid]操作里的边全加上,
加完之后,
如果边两端已经在同一个连通分量里,说明是更早就通了,拿着这些边s1,递归[l,mid],
否则说明加完了也没通,拿着这些边s2在[l,mid]缩点得到的图的基础上,递归[mid+1,r]
递归到最底层的时候把并查集合一合,计算一下大小
代码
#include <bits/stdc++.h>
using i64 = long long;
struct DSU {
std::vector<int> f, siz;
DSU() {}
DSU(int n) {
init(n);
}
void init(int n) {
f.resize(n);
std::iota(f.begin(), f.end(), 0);
siz.assign(n, 1);
}
int find(int x) {
while (x != f[x]) {
x = f[x] = f[f[x]];
}
return x;
}
bool same(int x, int y) {
return find(x) == find(y);
}
bool merge(int x, int y) {
x = find(x);
y = find(y);
if (x == y) {
return false;
}
siz[x] += siz[y];
f[y] = x;
return true;
}
int size(int x) {
return siz[find(x)];
}
};
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m, q;
std::cin >> n >> m >> q;
const int N = n + m;
std::vector<std::pair<int, int>> e(q);
for (int i = 0; i < q; i++) {
int u, v;
std::cin >> u >> v;
u--;
v--;
v += n;
e[i] = {u, v};
char c;
std::cin >> c;
if (c == 'R') {
std::swap(e[i].first, e[i].second);
}
}
i64 ans = 0;
DSU dsu(N);
std::vector<std::vector<int>> adj(N);
std::vector<int> dfn(N), low(N), bel(N), vis(N);
std::vector<int> stk;
int cur = 0, cnt = 0;
auto dfs = [&](auto &&self, int x) -> void {
dfn[x] = low[x] = cur++;
stk.push_back(x);
for (auto y : adj[x]) {
if (dfn[y] == -1) {
self(self, y);
low[x] = std::min(low[x], low[y]);
} else if (bel[y] == -1) {
low[x] = std::min(low[x], dfn[y]);
}
}
if (dfn[x] == low[x]) {
int y;
do {
y = stk.back();
bel[y] = cnt;
stk.pop_back();
} while (y != x);
cnt++;
}
};
auto work = [&](auto &&self, int l, int r, const std::vector<int> pv, const std::vector<int> pe) {
cur = cnt = 0;
for (auto x : pv) {
adj[x].clear();
dfn[x] = low[x] = bel[x] = -1;
vis[x] = 0;
}
for (auto i : pe) {
if (i >= r) {
continue;
}
auto [x, y] = e[i];
x = dsu.find(x);
y = dsu.find(y);
if (x == y) {
continue;
}
adj[x].push_back(y);
}
for (auto x : pv) {
if (dfn[x] == -1) {
dfs(dfs, x);
}
}
std::vector<int> npv, npe;
npv.reserve(pv.size());
npe.reserve(pe.size());
for (auto i : pe) {
if (i >= r) {
continue;
}
auto [x, y] = e[i];
x = dsu.find(x);
y = dsu.find(y);
if (x == y) {
continue;
}
if (bel[x] == bel[y]) {
if (!vis[x]) {
vis[x] = 1;
npv.push_back(x);
}
if (!vis[y]) {
vis[y] = 1;
npv.push_back(y);
}
npe.push_back(i);
}
}
if (r - l == 1) {
for (auto i : npe) {
auto [x, y] = e[i];
x = dsu.find(x);
y = dsu.find(y);
if (x == y) {
continue;
}
int sx = dsu.size(x);
int sy = dsu.size(y);
if (sx > 1) {
ans -= 1LL * sx * sx;
}
if (sy > 1) {
ans -= 1LL * sy * sy;
}
dsu.merge(y, x);
sx += sy;
ans += 1LL * sx * sx;
}
std::cout << ans << "\n";
return;
}
int m = (l + r) / 2;
self(self, l, m, npv, npe);
self(self, m, r, npv, npe);
};
std::vector<int> pv(N), pe(q);
std::iota(pv.begin(), pv.end(), 0);
std::iota(pe.begin(), pe.end(), 0);
work(work, 0, q, pv, pe);
return 0;
}