【题面自行脑补】
加强版。
Kruskal / LCT
本题可以用 LCT、Kruskal 重构树,但是由于至多加 1000 1000 1000 条边,所以建一个最小生成树直接暴力重构即可。
最小边最大,考虑最小生成树,Prim?懒得打,用 Kruskal。
对于每一个加边操作,重新跑一次 Kruskal,在最小生成树上倍增,跑 LCA。
用倍增维护一下即可。
namespace zqh {
const int N = 100005;
struct edge {
int u, v, w;
};
il bool operator<(edge a, edge b) {
return a.w < b.w;
}
int f[N][17], h[N][17], dep[N];
vector<pii> g[N];
vector<edge> e, e2;
struct dsu {
int fa[N];
dsu() {}
il void init(int n) {
for (rg int i = 1; i <= n; i++) {
fa[i] = i;
}
}
il int find(int n) {
return n == fa[n] ? n : fa[n] = find(fa[n]);
}
} d;
int n, m;
il void kruskal() {
for (int i = 1; i <= n; i++) g[i].clear();
d.init(n);
sort(e.begin(), e.end());
e2.clear();
int cnt = 0;
for (edge p : e) {
int fu = d.find(p.u), fv = d.find(p.v);
if (fu == fv) continue;
cnt++;
g[p.u].push_back({p.v, p.w});
g[p.v].push_back({p.u, p.w});
e2.push_back({p.u, p.v, p.w});
d.fa[fu] = fv;
if (cnt == n - 1) break;
}
e2.push_back(e.back());
e = e2;
}
il void refa(int u, int fa) {
f[u][0] = fa;
dep[u] = dep[fa] + 1;
for (pii p : g[u]) {
int x = p.first, y = p.second;
if (x == fa) continue;
refa(x, u);
h[x][0] = y;
}
}
il void reset() {
kruskal();
for (rg int i = 1; i <= n; i++) {
for (rg int j = 1; j <= 12; j++) {
f[i][j] = 0;
}
}
refa(1, 0);
// for (int i = 1; i <= n; i++) {
// cout << f[i][0] << endl;
// }
for (rg int i = 1; i <= 12; i++) {
for (rg int j = 1; j <= n; j++) {
f[j][i] = f[f[j][i - 1]][i - 1];
h[j][i] = max(h[j][i - 1], h[f[j][i - 1]][i - 1]);
// cout << "(" << j << " " << i << " " << f[j][i] << " " << h[j][i] << ")\n";
}
}
}
il int lca(int u, int v) {
if (dep[u] < dep[v]) swap(u, v);
// int base = -1;
int ans = 0, lg = log2(dep[u]);
// while ((1 << (base + 1)) <= dep[u]) base++;
for (rg int i = lg; i >= 0; i--) {
if (dep[u] - (1 << i) >= dep[v]) {
ans = max(ans, h[u][i]);
// cout << u << " " << i << " " << f[u][i] << endl;
u = f[u][i];
}
}
if (u == v) return ans;
for (rg int i = lg; i >= 0; i--) {
if (f[u][i] != f[v][i]) {
ans = max(ans, h[u][i]);
u = f[u][i];
ans = max(ans, h[v][i]);
v = f[v][i];
}
}
return max(ans, max(h[u][0], h[v][0]));
}
il void init() {
cin >> n >> m;
for (rg int i = 1; i <= m; i++) {
int u, v, w;
cin >> u >> v >> w;
e.push_back({u, v, w});
}
}
il void solve() {
reset();
int q;
cin >> q;
while (q--) {
string s;
cin >> s;
if (s == "game") {
int x, y, z, w;
cin >> x >> y >> z >> w;
int p = lca(x, y), q = lca(z, w);
// cout << p << " " << q << endl;
if (p == q) {
cout << "Baozika\n";
} else cout << "madoka\n";
} else {
int u, v, w;
cin >> u >> v >> w;
e.push_back({u, v, w});
reset();
}
}
}
void main() {
init();
solve();
}
} // namespace zqh
后记
每跑一次 Kruskal,总会剩余几条不够优秀的边,这些边下一次跑的时候就不用考虑,直接把边新加进去即可。
时间复杂度: O ( n q + log n × q ) \mathcal O(nq + \log n \times q) O(nq+logn×q)。