P5163 WD与地图 题解
Description
CX 让 WD 研究的地图可以看做是 \(n\) 个点,\(m\) 条边的有向图,由于政府正在尝试优化人民生活,他们会废弃一些无用的道路来把省下的钱用于经济建设。
城市都有各自的发达程度 \(s_i\)。为了方便管理,政府将整个地图划分为一些地区,两个点 \(u,v\) 在一个地区当且仅当 \(u,v\) 可以互相到达。政府希望知道一些时刻某个地区的前 \(k\) 发达城市的发达程度总和,以此推断建设的情况。
也就是说,共有三个操作:
1 a b 表示政府废弃了从 \(a\) 连向 \(b\) 的边,保证这条边存在。
2 a b 表示政府把钱用于建设城市 \(a\),使其发达程度增加 \(b\)。
3 a b 表示政府希望知道 \(a\) 城市所在地区发达程度前 \(b\) 大城市的发达程度之和。如果地区中的城市不足 \(b\) 个输出该地区所有城市的发达程度总和。
\(n\leq 10^5,m\leq 2\times 10^5,q\leq 2\times 10^5\)
Solution
考虑时空倒流,如果无向图就直接并查集+线段树合并即可。但是这里是 scc,所以无法确定能否合并,并且如果现在一条边的两端点在不同 scc 不代表以后也不在。
注意到一条边如果在某一时刻在 scc 内部,那么以后它一直会在 scc 内部。
如果直接二分,每次把出现时间在 \([1,mid]\) 的边加进去跑 tarjan,但这样做是 \(O(m^2\log m)\),显然过不了。
考虑整体二分,当前时间区间为 \([l,r]\),假设已经用并查集维护好了 \([1,l-1]\) 的 scc 信息,那么对于 \([l,mid]\) 的边就只要在之前的 scc 之间连边跑 tarjan。
对于出现时间在 \([l,mid]\) 的边如果两端点在同一 scc 了,就放 \([l,mid]\),否则放 \([mid+1,r]\)。而出现时间 \(>mid\) 的边显然只能放 \([mid+1,r]\)。
至于保证并查集维护好了 \([1,l-1]\) 的 scc 信息,可以每次加完 \([l,mid]\) 的边后先递归右区间,然后用可撤销并查集回到 \(l-1\) 的时刻后再递归左区间即可。
时间复杂度:\(O(m\log^2 m+(m+q)\log m)\)。
Code
#include <bits/stdc++.h>
#define int int64_t
const int kMaxN = 1e5 + 5, kMaxM = 3e5 + 5;
struct Node {
int op, a, b;
} qq[kMaxM];
int n, m, q, tot, mm;
int s[kMaxN], t[kMaxM], unq[kMaxM];
std::pair<int, int> ed[kMaxM];
int getid(int x) { return std::lower_bound(unq + 1, unq + 1 + mm, x) - unq; }
void discrete() {
std::sort(unq + 1, unq + 1 + mm);
mm = std::unique(unq + 1, unq + 1 + mm) - (unq + 1);
}
namespace GETT {
int dfn[kMaxN], low[kMaxN];
bool ins[kMaxN];
std::stack<std::tuple<int, int, int>> stk;
std::vector<int> vec, G[kMaxN];
std::map<std::pair<int, int>, int> mp;
struct DSU {
int fa[kMaxN], rnk[kMaxN];
void init(int n) {
std::fill_n(rnk + 1, n, 1);
std::iota(fa + 1, fa + 1 + n, 1);
}
int find(int x) {
for (; x != fa[x]; x = fa[x]) {}
return x;
}
void unionn(int x, int y) {
int fx = find(x), fy = find(y);
if (fx == fy) return;
if (rnk[fx] > rnk[fy]) std::swap(fx, fy);
fa[fx] = fy;
int det = (rnk[fx] == rnk[fy]);
rnk[fy] += det;
stk.emplace(fx, fy, det);
}
} dsu;
void back(int t) {
for (; stk.size() > t; stk.pop()) {
auto [x, y, det] = stk.top();
dsu.rnk[y] -= det, dsu.fa[x] = x;
}
}
void dfs(int u) {
static int cnt = 0;
static std::stack<int> stk;
dfn[u] = low[u] = ++cnt, stk.emplace(u), ins[u] = 1;
for (auto v : G[u]) {
if (!dfn[v]) {
dfs(v), low[u] = std::min(low[u], low[v]);
} else if (ins[v]) {
low[u] = std::min(low[u], dfn[v]);
}
}
if (low[u] == dfn[u]) {
for (; !stk.empty();) {
int t = stk.top();
stk.pop();
ins[t] = 0, dsu.unionn(t, u);
if (t == u) break;
}
}
}
void solve(int l, int r, std::vector<int> &vec) {
if (l == r) {
std::vector<int> idx;
for (auto i : vec) {
int x = dsu.find(ed[i].first), y = dsu.find(ed[i].second);
G[x].emplace_back(y), idx.emplace_back(x), idx.emplace_back(y);
}
int lst = stk.size();
for (auto x : idx) {
if (!dfn[x]) dfs(x);
}
for (auto i : vec) {
if (dsu.find(ed[i].first) == dsu.find(ed[i].second)) {
t[i] = l;
}
}
back(lst);
for (auto x : idx) G[x].clear(), dfn[x] = low[x] = ins[x] = 0;
return;
}
int mid = (l + r) >> 1, lst = stk.size();
std::vector<int> idx, ls, rs;
for (auto i : vec) {
if (i <= mid) {
int x = dsu.find(ed[i].first), y = dsu.find(ed[i].second);
G[x].emplace_back(y), idx.emplace_back(x), idx.emplace_back(y);
}
}
for (auto x : idx) {
if (!dfn[x]) dfs(x);
}
for (auto i : vec) {
if (i <= mid) {
if (dsu.find(ed[i].first) == dsu.find(ed[i].second)) {
ls.emplace_back(i);
} else {
rs.emplace_back(i);
}
} else if (dsu.find(ed[i].first) != dsu.find(ed[i].second)) {
rs.emplace_back(i);
}
}
for (auto x : idx) G[x].clear(), dfn[x] = low[x] = ins[x] = 0;
solve(mid + 1, r, rs);
back(lst);
solve(l, mid, ls);
}
void solve() {
for (auto [p, o] : mp) {
if (o) ed[++tot] = p;
}
std::reverse(ed + 1, ed + 1 + tot);
std::fill_n(t + 1, tot, tot + 1);
for (int i = 1; i <= tot; ++i) vec.emplace_back(i);
dsu.init(n);
solve(1, tot, vec);
}
} // namespace GETT
namespace GETANS {
int sgt_tot, id[kMaxM], fa[kMaxN], rt[kMaxN], ls[kMaxN * 32], rs[kMaxN * 32], sum[kMaxN * 32], cnt[kMaxN * 32];
std::vector<std::tuple<int, int, int>> vec;
void pushup(int x) {
sum[x] = sum[ls[x]] + sum[rs[x]];
cnt[x] = cnt[ls[x]] + cnt[rs[x]];
}
void update(int &x, int l, int r, int ql, int v) {
if (!x) x = ++sgt_tot;
sum[x] += v * unq[ql], cnt[x] += v;
if (l == r) return;
int mid = (l + r) >> 1;
if (ql <= mid) update(ls[x], l, mid, ql, v);
else update(rs[x], mid + 1, r, ql, v);
}
int merge(int x, int y, int l, int r) {
if (!x || !y) return x + y;
if (l == r) {
sum[x] += sum[y], cnt[x] += cnt[y];
return x;
}
int mid = (l + r) >> 1;
ls[x] = merge(ls[x], ls[y], l, mid), rs[x] = merge(rs[x], rs[y], mid + 1, r);
pushup(x);
return x;
}
int query(int x, int l, int r, int k) {
if (!x) return 0;
else if (k >= cnt[x]) return sum[x];
else if (l == r) return k * unq[l];
int mid = (l + r) >> 1;
if (k <= cnt[rs[x]]) return query(rs[x], mid + 1, r, k);
else return sum[rs[x]] + query(ls[x], l, mid, k - cnt[rs[x]]);
}
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void unionn(int x, int y) {
int fx = find(x), fy = find(y);
if (fx != fy) fa[fx] = fy, rt[fy] = merge(rt[fy], rt[fx], 1, mm);
}
void solve() {
int now = tot;
for (int i = 1; i <= tot; ++i) {
vec.emplace_back(t[i], ed[i].first, ed[i].second);
}
std::sort(vec.begin(), vec.end());
for (int i = 1; i <= q; ++i) {
id[i] = now;
if (qq[i].op == 1) --now;
}
for (int i = 1; i <= n; ++i) {
fa[i] = i, update(rt[i], 1, mm, getid(s[i]), 1);
}
int pos = -1;
std::vector<int> ans;
for (int i = q; i; --i) {
for (; pos + 1 < vec.size() && std::get<0>(vec[pos + 1]) <= id[i];) {
auto [t, u, v] = vec[++pos];
unionn(u, v);
}
if (qq[i].op == 2) {
int fi = find(qq[i].a);
update(rt[fi], 1, mm, getid(s[qq[i].a]), -1);
s[qq[i].a] -= qq[i].b;
update(rt[fi], 1, mm, getid(s[qq[i].a]), 1);
} else if (qq[i].op == 3) {
int fi = find(qq[i].a);
ans.emplace_back(query(rt[fi], 1, mm, qq[i].b));
}
}
std::reverse(ans.begin(), ans.end());
for (auto x : ans) std::cout << x << '\n';
}
} // namespace GETANS
void dickdreamer() {
std::cin >> n >> m >> q;
for (int i = 1; i <= n; ++i) {
std::cin >> s[i];
unq[++mm] = s[i];
}
for (int i = 1; i <= m; ++i) {
int u, v;
std::cin >> u >> v;
GETT::mp[{u, v}] = 1;
}
for (int i = 1; i <= q; ++i) {
std::cin >> qq[i].op >> qq[i].a >> qq[i].b;
if (qq[i].op == 1) {
ed[++tot] = {qq[i].a, qq[i].b}, GETT::mp[{qq[i].a, qq[i].b}] = 0;
} else if (qq[i].op == 2) {
s[qq[i].a] += qq[i].b;
unq[++mm] = s[qq[i].a];
}
}
discrete(), GETT::solve(), GETANS::solve();
}
int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

浙公网安备 33010602011771号