P10238 [yLCPC2024] F. PANDORA PARADOXXX

Fire_Raku發表於2024-04-05

P10238 [yLCPC2024] F. PANDORA PARADOXXX

並查集維護連通性+結論+資料結構維護距離

題目的操作是刪邊通常複雜,並且不強制線上,所以離線倒過來加邊。

題目要求的就是當前所有連通塊的直徑的最大值,考慮加邊後兩個連通塊合併後直徑的變化。有結論:合併後的連通塊的直徑兩端點一定是合併前兩個連通塊的直徑端點其二。

證明:設兩個連通塊的端點分別為 \((a,b)\)\((c,d)\)。假如合併後直徑沒有跨越兩個連通塊,那麼直徑顯然還是 \((a,b)\)\((c,d)\);否則設連線兩個連通塊的邊為 \((x,y)\),根據直徑的性質,到 \(x\) 最遠的點為 \(a\)\(b\),到 \(y\) 最遠的點為 \(c\)\(d\)。那麼直徑的端點還是其二。

於是考慮維護連通塊中的直徑端點。初始每個點為一個連通塊,每次加邊求出新的直徑。這些可以用並查集維護。關於每條路徑的距離如何求,由於合併完的連通塊一定是原先樹的一部分,所以我們在原先的樹上預處理出 \(dis\)\(lca\) 等資訊即可。

複雜度 \(O(n\log n)\),帶一些小常數。

#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fi first
#define se second
#define pb push_back

typedef long long i64;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
const int N = 1e5 + 10;
int n, q, cnt;
i64 ret;
int h[N];
std::vector<pii> E, G;
i64 dep[N], sz[N], top[N], fa[N], son[N], dis[N], ans[N];
struct node {
	int to, nxt; i64 w;
} e[N << 1];
void add(int u, int v, i64 w) {
	e[++cnt].to = v, e[cnt].nxt = h[u], e[cnt].w = w, h[u] = cnt;
}
void dfs1(int u, int f) {
	dep[u] = dep[f] + 1;
	fa[u] = f;
	sz[u] = 1;
	for(int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].to, w = e[i].w;
		if(v == f) continue;
		dis[v] = dis[u] + w; 
		dfs1(v, u);
		sz[u] += sz[v];
		if(sz[v] > sz[son[u]]) son[u] = v;	
	}
}
void dfs2(int u, int topf) {
	top[u] = topf;
	if(!son[u]) return;
	dfs2(son[u], topf);
	for(int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].to;
		if(v == fa[u] || v == son[u]) continue;
		dfs2(v, v);
	}
}
int lca(int u, int v) {
	while(top[u] != top[v]) {
		if(dep[top[u]] < dep[top[v]]) std::swap(u, v);
		u = fa[top[u]];
	}
	if(dep[u] > dep[v]) std::swap(u, v);
	return u;
}
i64 D(int u, int v) {
	int rt = lca(u, v);
	return dis[u] + dis[v] - 2 * dis[rt];
}

struct Union {
	int fa[N], l[N], r[N];
	int find(int x) {
		if(x != fa[x]) fa[x] = find(fa[x]);
		return fa[x];
	}
} U;
void mg(int u, int v) {
	int fu = U.find(u), fv = U.find(v);
	pii ve[6] = {{U.l[fu], U.r[fu]}, {U.l[fu], U.l[fv]}, {U.l[fu], U.r[fv]},
				 {U.r[fu], U.l[fv]}, {U.r[fu], U.r[fv]}, {U.l[fv], U.r[fv]}};
	i64 res = 0, ru, rv;
	for(int i = 0; i < 6; i++) {
 		if(res < D(ve[i].fi, ve[i].se)) {
 			res = D(ve[i].fi, ve[i].se), ru = ve[i].fi, rv = ve[i].se; 
 		}
	}
	ret = std::max(ret, res);
	U.fa[fv] = fu;
	U.l[fu] = ru, U.r[fu] = rv;
}
void Solve() {
	std::cin >> n >> q;

	E.pb({0, 0}), G.pb({0, 0});
	for(int i = 1; i < n; i++) {
		int u, v; i64 w;
		std::cin >> u >> v >> w;
		add(u, v, w), add(v, u, w);
		E.pb({u, v});
	}
	dfs1(1, 0), dfs2(1, 1);
	for(int i = 1; i <= q; i++) {
		int x;
		std::cin >> x;
		G.pb(E[x]), E[x] = {0, 0};
	}
	for(auto x : E) {
		if(x.fi) G.pb({x.fi, x.se});
	}
	std::reverse(G.begin() + 1, G.end());
	for(int i = 1; i <= n; i++) U.fa[i] = U.l[i] = U.r[i] = i;
	for(int i = 1; i < n; i++) {
		ans[i] = ret;
		mg(G[i].fi, G[i].se);
	}

	for(int i = n - 1; i >= n - q; i--) std::cout << ans[i] << "\n";
	E.clear(), G.clear();
	for(int i = 1; i <= n; i++) U.fa[i] = U.l[i] = U.r[i] = h[i] = dep[i] = dis[i] = son[i] = fa[i] = sz[i] = top[i] = ans[i] = 0; 
	cnt = ret = 0;
}
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int T;
    std::cin >> T;

	while(T--) Solve();

	return 0;
}

相關文章