玩游戏 题解

【题面自行脑补】

加强版

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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值