P5163 WD与地图 题解

Description

CX 让 WD 研究的地图可以看做是 nn 个点,mm 条边的有向图,由于政府正在尝试优化人民生活,他们会废弃一些无用的道路来把省下的钱用于经济建设。

城市都有各自的发达程度 sis_i。为了方便管理,政府将整个地图划分为一些地区,两个点 u,vu,v 在一个地区当且仅当 u,vu,v 可以互相到达。政府希望知道一些时刻某个地区的前 kk 发达城市的发达程度总和,以此推断建设的情况。

也就是说,共有三个操作:

1 a b 表示政府废弃了从 aa 连向 bb 的边,保证这条边存在。

2 a b 表示政府把钱用于建设城市 aa,使其发达程度增加 bb

3 a b 表示政府希望知道 aa 城市所在地区发达程度前 bb 大城市的发达程度之和。如果地区中的城市不足 bb 个输出该地区所有城市的发达程度总和。

n105,m2×105,q2×105n\leq 10^5,m\leq 2\times 10^5,q\leq 2\times 10^5

Solution

考虑时空倒流,如果无向图就直接并查集+线段树合并即可。但是这里是 scc,所以无法确定能否合并,并且如果现在一条边的两端点在不同 scc 不代表以后也不在。

注意到一条边如果在某一时刻在 scc 内部,那么以后它一直会在 scc 内部。

如果直接二分,每次把出现时间在 [1,mid][1,mid] 的边加进去跑 tarjan,但这样做是 O(m2logm)O(m^2\log m),显然过不了。

考虑整体二分,当前时间区间为 [l,r][l,r],假设已经用并查集维护好了 [1,l1][1,l-1] 的 scc 信息,那么对于 [l,mid][l,mid] 的边就只要在之前的 scc 之间连边跑 tarjan。

对于出现时间在 [l,mid][l,mid] 的边如果两端点在同一 scc 了,就放 [l,mid][l,mid],否则放 [mid+1,r][mid+1,r]。而出现时间 >mid>mid 的边显然只能放 [mid+1,r][mid+1,r]

至于保证并查集维护好了 [1,l1][1,l-1] 的 scc 信息,可以每次加完 [l,mid][l,mid] 的边后先递归右区间,然后用可撤销并查集回到 l1l-1 的时刻后再递归左区间即可。

时间复杂度:O(mlog2m+(m+q)logm)O(m\log^2 m+(m+q)\log m)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
#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;
}

P5163 WD与地图 题解
https://sobaliuziao.github.io/2024/02/20/post/b818a32f.html
作者
Egg_laying_master
发布于
2024年2月20日
许可协议