「NOI2018」情報中心(線段樹合併)(虛樹)

傳送門

神題一道
題意:
一棵樹,有邊權,mm 條路徑,路徑有代價,選擇兩條相交的路徑使得路徑的並的邊權 - 總代價最大
n5e4,m1e5,n1e6,m2e6,8sn\le 5e4,m\le1e5,\sum n\le1e6,\sum m\le 2e6,8s

直接考慮 S1,S2S_1,S_2 的性質

S1S_1
路徑的 lcalca 兩兩不同,於是路徑的交只有可能是直上直下
在這裏插入圖片描述
我們枚舉紅點,那麼這個時候的貢獻應該是
len1+len2cost1cost2(depRmax(depG,depB))len_1+len_2-cost_1-cost_2-(dep_{R}-max(dep_G,dep_B))
考慮樹上差分後線段樹合併,合併的時候考慮兩個不同子樹路徑的拼接
現在的問題就是處理哪個在上面有點煩
發現可以按深度維護線段樹,維護 lencostlen-costlencost+deplen-cost+dep 的最大值
線段樹合併的時候考慮一個的左子樹有一個右子樹拼接,就可以很巧妙地知道誰在上方
複雜度 O((M+N)log(N))O((M+N)log(N))

S2S_2
在這裏插入圖片描述
最後的貢獻是:
12(lena+lenb2costa2costb+depa+depbdeplca(a,b)+dis(pa,pb))\frac{1}{2}(len_a+len_b-2*cost_a-2*cost_b+dep_a+dep_b-dep_{lca(a,b)}+dis(p_a,p_b))
我們枚舉 lca(a,b)lca(a,b) 發現除 dis(pa,pb)dis(p_a,p_b)a,ba,b 獨立
如果我們令 pap_a 的點權爲 lena2costa+depalen_a-2*cost_a+dep_a 的話,那麼問題就是找一對最遠點
現在的問題就是支持集合合併,合併的時候詢問最遠點
合併後的最遠點一定是兩個集合四個最遠點的其中兩個,於是可以很方便維護
對於所有路徑的 lcalca 不在一個點時,把路徑存在 lcalca 處然後大力建虛樹就可以了
複雜度 O(Mlog(N))O(Mlog(N))


兩種方法都有很巧妙的地方
第一種是考慮線段樹合併的時候更新答案就可以知道誰的深度更深
第二種是將問題轉換爲最遠點對,對於一個點集的最遠點對只需要維護兩個點並支持快速合併

#include<bits/stdc++.h>
#define cs const
#define mp make_pair
using namespace std;
typedef long long ll;
cs ll INF = 1e18;
void Mx(ll &a, ll b){ if(a < b) a = b; }
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
cs int N = 1e5 + 50;
int T, n, m; ll ans = -INF;
int fi[N], nxt[N], to[N], w[N], tot;
void add(int x, int y, int z){ nxt[++tot] = fi[x], fi[x] = tot, to[tot] = y, w[tot] = z; }
int lg[N], st[N][20], in[N], sgn, dep[N]; ll d[N];
struct cp{ int u; ll w; cp(int _u = 0, ll _w = 0){u = _u, w = _w; } };
void dfs(int u, int fa){
	st[in[u] = ++sgn][0] = u; 
	for(int i = fi[u]; i; i = nxt[i]){
		int t = to[i]; if(t == fa) continue;
		dep[t] = dep[u] + 1; d[t] = d[u] + (ll)w[i];
		dfs(t, u); st[++sgn][0] = u;
	}
}
int ck(int x, int y){ return dep[x] < dep[y] ? x : y; }
int lca(int x, int y){
	int l = in[x], r = in[y];
	if(l > r) swap(l, r); 
	int d = lg[r - l + 1];
	return ck(st[l][d], st[r-(1<<d)+1][d]);
}
ll dist(int x, int y){ return d[x] + d[y] - 2 * d[lca(x, y)]; }
namespace S1{
	cs int N = ::N * 25;
	ll mx1[N], mx2[N], delta;
	int ls[N], rs[N], rt[::N], nd;
	vector<cp> G[N];
	#define mid ((l+r)>>1)
	void pushup(int x){ 
		mx1[x] = max(mx1[ls[x]], mx1[rs[x]]);
		mx2[x] = max(mx2[ls[x]], mx2[rs[x]]);
	}
	void ins(int &x, int l, int r, int p, ll v1, ll v2){
		if(!x){
			x = ++nd; ls[x] = rs[x] = 0;
			mx1[x] = mx2[x] = -INF;
		} 
		if(l == r){ Mx(mx1[x], v1); Mx(mx2[x], v2); return; }
		if(p <= mid) ins(ls[x],l,mid,p,v1,v2);
		else ins(rs[x],mid+1,r,p,v1,v2); pushup(x);
	}
	void dec(int &x, int l, int r, int p){
		if(!x) return;
		if(l == r){ x = 0; return; }
		if(p <= mid) dec(ls[x],l,mid,p);
		else dec(rs[x],mid+1,r,p);
		pushup(x);
	}
	int merge(int &x, int y, int l = 1, int r = n){
		if(!x||!y) return x|y;
		if(l == r){ Mx(mx1[x],mx1[y]); Mx(mx2[x],mx2[y]); return x; }
		ans = max(ans, mx1[ls[x]] + mx2[rs[y]] - delta);
		ans = max(ans, mx2[rs[x]] + mx1[ls[y]] - delta);
		ls[x] = merge(ls[x], ls[y], l, mid);
		rs[x] = merge(rs[x], rs[y], mid+1, r); pushup(x); return x;
	}
	void dfs(int u, int fa){
		for(int i = fi[u]; i; i = nxt[i]){
			int t = to[i]; if(t == fa) continue; dfs(t, u);
		}
		delta = d[u];
		for(int i = fi[u]; i; i = nxt[i]){
			int t = to[i]; if(t == fa) continue;
			dec(rt[t], 1, n, dep[u]);
			rt[u] = merge(rt[u], rt[t]);
		}
		for(cp v:G[u]){
			int r = 0; ins(r, 1, n, dep[v.u], v.w, v.w + d[v.u]);
			rt[u] = merge(rt[u], r);
		}
	}
	void Solve(){
		mx1[0] = mx2[0] = -INF;
		dfs(1, 0); nd = 0;
		for(int i = 1; i <= n; i++) rt[i] = 0, G[i].clear();
	}
}
namespace S2{
	struct Set{ 
		cp x, y; ll d; 
		bool operator < (cs Set &a) cs{ return d < a.d; }
	};
	Set f[N];
	struct Path{ int u, v; ll w; } ;
	vector<Path> G[N];
	int sta[N], arr[N], top, now, rt;
	bool cmp(int x, int y){ return in[x] < in[y]; }
	Set calc(cp A, cp B){
		ll di = dist(A.u, B.u) + A.w + B.w;
		ans = max(ans, di / 2 - d[now]);
		return (Set){A, B, di};
	}
	void merge(Set &A, Set &B){
		if(A.d == -INF){ A = B; B.d = -INF; return; }
		if(B.d == -INF) return;
		if(now ^ rt){
			Set P = max(calc(A.x,B.x),calc(A.x,B.y));
			P = max(P, max(calc(A.y,B.x), calc(A.y,B.y)));
			A = max(A, max(P, B));
		} B.d = -INF;
	}
	void work(cs vector<Path> &vec){
		int top = 0, sz = 0; 
		for(Path t:vec){
			arr[++sz] = t.u;
			arr[++sz] = t.v;
			cp a(t.u, d[t.v] + t.w);
			cp b(t.v, d[t.u] + t.w);
			Set x = (Set){a, a, a.w << 1}, y = (Set){b, b, b.w << 1};
			merge(f[now = t.u], y); merge(f[now = t.v], x);
		}
		sort(arr + 1, arr + sz + 1, cmp);
		sta[++top] = arr[1];
		for(int i = 2; i <= sz; i++) if(arr[i] != arr[i-1]){
			int p = arr[i], l = lca(p, sta[top]);
			while(top > 1 && dep[sta[top - 1]] >= dep[l])
			merge(f[now = sta[top - 1]], f[sta[top]]), --top;
			if(sta[top] ^ l) merge(f[now = l], f[sta[top]]), sta[top] = l;
			sta[++top] = p;
		} while(top > 1) merge(f[now = sta[top - 1]], f[sta[top]]), --top;
		f[sta[1]].d = -INF;
	}
	void Solve(){
		for(int i = 1; i <= n; i++) f[i].d = -INF;
		for(int i = 1; i <= n; i++) if(G[i].size() > 1) rt = i, work(G[i]);
		for(int i = 1; i <= n; i++) G[i].clear();
	}
}
void Clear(){
	ans = -INF;
	memset(fi, 0, sizeof(int)*(n+1)); tot = sgn = 0;	
}
void Solve(){
	n = read();
	for(int i = 1; i < n; i++){
		int x = read(), y = read(), z = read();
		add(x, y, z); add(y, x, z);
	} dep[1] = 1; dfs(1, 0);

	for(int i = 2; i <= sgn; i++) lg[i] = lg[i>>1] + 1;
	for(int j = 1; (1<<j)<=sgn; j++)
	for(int i = 1; i+(1<<j)-1 <= sgn; i++)
	st[i][j] = ck(st[i][j-1], st[i+(1<<j-1)][j-1]);
	
	m = read();
	for(int i = 1; i <= m; i++){
		int u = read(), v = read(); ll w; scanf("%lld", &w);
		if(u == v) continue;
		int l = lca(u, v); ll dis = dist(u, v);
		if(v ^ l) S1::G[v].push_back(cp(l, dis - w));
		if(u ^ l) S1::G[u].push_back(cp(l, dis - w));
		S2::G[l].push_back((S2::Path){u, v, dis - 2 * w});
	}
	S1::Solve();
	S2::Solve();
	if(ans < -1e17) puts("F");
	else cout << ans << '\n';
}
int main(){
	freopen("center.in","r",stdin);
	freopen("center.out","w",stdout);
	T = read();
	while(T--) Solve(), Clear();
	return 0;
}
發佈了651 篇原創文章 · 獲贊 98 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章