zoukankan      html  css  js  c++  java
  • 【题解】 「CTSC2018」暴力写挂 点分治+虚树+树形dp LOJ2553

    Legend

    Link ( extrm{to LOJ})

    给定两棵 (n) 个节点的树 (T,T'),边有边权 (v_i,v'_i)。定义两点 ((x,y)) 之间的距离为:

    [dist(x,y)=dep_{x}+dep_{y}-(dep_{operatorname{LCA}(x,y)} + dep'_{operatorname{LCA}'(x,y)}) ]

    (max dist(x,y))

    (n le 366666)(|v_i|,|v_i'| le 2017011328)

    Editorial

    式子中有两个 ( m{LCA}),让人很不爽。因为带有 (') 的只有后面一项,肯定去不掉,只能去掉前面一个:

    (dist(x,y)=dfrac{1}{2}left(dep_{x}+dep_{y}+w(x,y)-2dep'_{operatorname{LCA}'(x,y)} ight))

    这里的 (w(x,y)) 指的是树上两点真实距离。

    请先无视那个 (dfrac{1}{2}),思考怎么计算后面一坨。

    不要被这个式子吓倒了,如果我们不考虑最后一项怎么做?

    一个简单的树形 ( m{dp}),假设你现在在 (x),对于每一个子节点 (i) 维护子树内的最大 (a_i=dep_i+w(x,i))。然后选择不同的儿子进行合并答案就可以了。

    具体来说就是,只要算出 (max a_i+a_j (i,j in son_x ,i ot= j)) 就行了。

    这个显然可以 (O(n)) 做。

    那么加入最后一项为我们带来了一些不便——我们不能通过树上的前缀差分同时快速拼凑出两棵树的贡献。

    所以我们必然要对于某一棵树进行数据结构的维护。

    最终我们选择了点分治来对树 (T) 进行分治,这样依然可以通过前缀差分快速算这一棵树对于答案式子的贡献

    设当前分治中心是 (x),记录一个 (v_i=w(i,x)+dep_i),那么属于两棵不同子树的节点 (i,j) 的贡献就是 (dist(i,j)=v_i+v_j-2dep'_{operatorname{LCA}'(x,y)})

    这时,在树 (T) 上的部分已经处理完了,被我们整合成了一个叫做 (v_i) 的信息。

    那么接下来我们要算的是属于不同子树的节点两两之间在树 (T') 上的贡献 (-2dep'_{operatorname{LCA}'(x,y)})

    显然的想法是枚举 ( m{LCA}),我们可以用树形 ( m{dp}) 来实现:

    假设你现在在 (x),维护子树内的最大和次大 (v_i),用 (a_i,a'_i) 表示,并要求它们不能属于同一棵(点分树的)子树(显然点分治在当前分治中心不能计算同一子树的贡献)。

    答案就是 (a_i+a'_i-2dep'_{x})

    如果你把这个树形 ( m{dp}) 搬到虚树上来的话,复杂度就变成了总体 (O(n log n)),或总体 (O(n))

    加上点分治的复杂度,本题复杂度就是 (O(n log n^2)) 或者 (O(n log n))

    Code

    我很懒,写的是 (O(n log^2 n)) 的。

    倍增的时候看清楚 (i)(i+1)……今天为了一个 (operatorname{LCA}) 调试了一上午。。。。。

    注意特判 (x=y) 的情况。

    #include <bits/stdc++.h>
    
    #define LL long long
    #define debug(...) fprintf(stderr ,__VA_ARGS__)
    #define __FILE(x)
    	freopen(#x".in" ,"r" ,stdin);
    	freopen(#x".out" ,"w" ,stdout)
    
    const int MX = 366666 + 23;
    int read(){
    	char k = getchar(); int x = 0 ,flg = 1;
    	while(k < '0' || k > '9') flg *= (k == '-' ? -1 : 1) ,k = getchar();
    	while(k >= '0' && k <= '9') x = x * 10 + k - '0' ,k = getchar();
    	return x * flg;
    }
    
    LL Ans = LLONG_MIN;
    int T1[MX] ,T2[MX] ,tot = 1;
    struct edge{
    	int node ,next ,w;
    }h[MX << 2];
    void addedge(int u ,int v ,int w ,int *head ,int flg = 1){
    	h[++tot] = (edge){v ,head[u] ,w} ,head[u] = tot;
    	if(flg) addedge(v ,u ,w ,head ,false);
    }
    
    void GetDepth(int x ,int f); // Get depth array of tree <1>
    void GetRoot(int x ,int f);  // For vertex divivde & conquer 
    void Stain(int x ,int f ,int col ,LL w); // For vertex divivde & conquer 
    void Doit(int x); // For vertex divivde & conquer 
    void DivAndCon(int x); // For vertex divivde & conquer 
    void BuildVoidTree();
    void GetOrder(int x ,int f); // Get order of node(first and last occurrences) tree<2>
    
    namespace FOR_LCA{
    	int lg2[MX << 1];
    	// void GetFA(int x); // Get father array of Tree <2>
    	int rmq(int x ,int y);
    	int LCA(int x ,int y); // Get LCA(x ,y) of Tree <2>
    	int dfn[MX] ,cnt ,seq[20][MX << 1] ,seqcnt ,refer[MX];
    	int firstOccurrence[MX];
    	void init_log2(){
    		lg2[0] = -1;
    		for(int i = 1 ; i < MX * 2 ; ++i)
    			lg2[i] = lg2[i - 1] + (i == (i & -i));
    		for(int i = 1 ; i <= 19 ; ++i){
    			for(int j = 1 ; j + (1 << (i - 1)) - 1 <= seqcnt ; ++j){
    				seq[i][j] = std::min(seq[i - 1][j] ,seq[i - 1][j + (1 << (i - 1))]);
    			}
    		}
    	}
    	void GetEuler(int x ,int f = 0){
    		static int *head = T2;
    		seq[0][++seqcnt] = dfn[x] = ++cnt;
    		firstOccurrence[x] = seqcnt;
    		refer[cnt] = x;
    		for(int i = head[x] ,d ; i ; i = h[i].next){
    			if((d = h[i].node) == f) continue;
    			GetEuler(d ,x);
    			seq[0][++seqcnt] = dfn[x];
    		}
    	}
    	int rmq(int l ,int r){
    		if(l > r) std::swap(l ,r);
    		int len = lg2[r - l + 1];
    		return std::min(seq[len][l] ,seq[len][r - (1 << len) + 1]);
    	}
    	int LCA(int l ,int r){
    		return refer[rmq(firstOccurrence[l] ,firstOccurrence[r])];
    	}
    	void Main(){
    		GetEuler(1);
    		init_log2();
    	}
    }using FOR_LCA::LCA;
    
    LL dep[MX];
    void GetDepth(int x ,int f){
    	static int *head = T1;
    	for(int i = head[x] ,d ; i ; i = h[i].next){
    		if((d = h[i].node) == f) continue;
    		dep[d] = dep[x] + h[i].w ,GetDepth(d ,x);
    	}
    }
    
    int R ,Size[MX] ,mxsz[MX] ,TreeSize ,vis[MX];
    void GetRoot(int x ,int f){
    	static int *head = T1;
    	Size[x] = 1 ,mxsz[x] = 0;
    	for(int i = head[x] ,d ; i ; i = h[i].next){
    		if(vis[d = h[i].node] || d == f) continue;
    		GetRoot(d ,x) ,Size[x] += Size[d];
    		mxsz[x] = std::max(mxsz[x] ,Size[d]);
    	}
    	mxsz[x] = std::max(mxsz[x] ,TreeSize - Size[x]);
    	// debug("mxsz[%d] = %d
    " ,x ,mxsz[x]);
    	if(mxsz[x] < mxsz[R]) R = x;
    }
    
    int color[MX];
    LL val[MX];
    
    struct POINT{
    	int id ,tim;
    	bool operator <(const POINT &B)const{
    		return tim < B.tim;
    	}
    }S[MX << 1];
    
    int app[2][MX] ,appcnt;
    LL depT2[MX];
    void GetOrder(int x ,int f){
    	static int *head = T2;
    	app[0][x] = ++appcnt;
    	for(int i = head[x] ,d ; i ; i = h[i].next){
    		if((d = h[i].node) == f) continue;
    		depT2[d] = depT2[x] + h[i].w;
    		GetOrder(d ,x);
    	}
    	app[1][x] = ++appcnt;
    }
    
    int Scnt ,Suse[MX];
    void addPOINT(int x){
    	if(Suse[x]++) return;
    	++Scnt ,S[Scnt] = (POINT){x ,app[0][x]};
    	++Scnt ,S[Scnt] = (POINT){-x ,app[1][x]};
    }
    
    struct DPclass{
    	LL mx[2];
    	int color[2];
    	DPclass(){
    		mx[0] = mx[1] = -(1LL << 50);
    		color[0] = rand() ,color[1] = rand();
    	}
    	DPclass(LL mx0 ,LL color0){
    		mx[0] = mx0 ,mx[1] = -(1LL << 50);
    		color[0] = color0 ,color[1] = rand();
    	}
    	LL operator +(DPclass B)const{
    		if(color[0] == B.color[0]){
    			return std::max(mx[0] + B.mx[1] ,B.mx[0] + mx[1]);
    		}
    		return mx[0] + B.mx[0];
    	}
    	
    	void output(){
    		printf("{%lld ,%d}, {%lld ,%d}
    " ,mx[0] ,color[0] ,mx[1] ,color[1]);
    	}
    }dp[MX];
    
    DPclass max(DPclass A ,DPclass B){
    	DPclass C;
    	if(A.color[0] == B.color[0]){
    		C.color[0] = A.color[0];
    		C.mx[0] = std::max(A.mx[0] ,B.mx[0]);
    		C.mx[1] = std::max(A.mx[1] ,B.mx[1]);
    		C.color[1] = (C.mx[1] == A.mx[1] ? A.color[1] : B.color[1]);
    	}
    	else{
    		if(A.mx[1] >= B.mx[0]) return A;
    		if(B.mx[1] >= A.mx[0]) return B;
    		C.mx[0] = A.mx[0];
    		C.color[0] = A.color[0];
    		C.mx[1] = B.mx[0];
    		C.color[1] = B.color[0];
    		if(C.mx[0] < C.mx[1]){
    			std::swap(C.mx[0] ,C.mx[1]);
    			std::swap(C.color[0] ,C.color[1]);
    		}
    	}
    	return C;
    }
    
    int stk[MX] ,stkcnt;
    void BuildVoidTree(){
    	std::sort(S + 1 ,S + 1 + Scnt);
    	int curScnt = Scnt;
    	for(int i = 1 ; i < curScnt ; ++i){
    		// debug("LCA(%d ,%d) = %d
    " ,std::abs(S[i].id) ,std::abs(S[i + 1].id) ,LCA(std::abs(S[i].id) ,std::abs(S[i + 1].id)));
    		int lca = LCA(std::abs(S[i].id) ,std::abs(S[i + 1].id));
    		addPOINT(lca);
    	}
    	addPOINT(1);
    	std::sort(S + 1 ,S + 1 + Scnt);
    	for(int i = 1 ; i <= Scnt ; ++i){
    		if(S[i].id > 0){
    			int x = S[i].id;
    			stk[++stkcnt] = x;
    			if(color[x]){
    				dp[x] = DPclass(val[x] ,color[x]);
    			}
    			else dp[x] = DPclass();
    		}
    		else{
    			int x = stk[stkcnt--];
    			if(stkcnt){
    				Ans = std::max(Ans ,dp[x] + dp[stk[stkcnt]] - 2 * depT2[stk[stkcnt]]);
    				dp[stk[stkcnt]] = max(dp[stk[stkcnt]] ,dp[x]);
    			}
    			Suse[x] = false;
    			color[x] = 0;
    		}
    	}
    
    	Scnt = 0;	
    }
    
    void Stain(int x ,int f ,int col ,LL w){
    	addPOINT(x);
    	static int *head = T1;
    	color[x] = col;
    	val[x] = w + dep[x];
    	for(int i = head[x] ,d ; i ; i = h[i].next){
    		if((d = h[i].node) == f || vis[d]) continue;
    		Stain(d ,x ,col ,w + h[i].w);
    	}
    }
    
    void Doit(int x){
    	static int *head = T1;
    	int color_cnt = 0;
    	color[x] = ++color_cnt;
    	val[x] = dep[x];
    	addPOINT(x);
    	for(int i = head[x] ,d ; i ; i = h[i].next){
    		if(vis[d = h[i].node]) continue;
    		Stain(d ,x ,++color_cnt ,h[i].w);
    	}
    	BuildVoidTree();
    }
    
    void DivAndCon(int x){
    	static int *head = T1;
    	vis[x] = true;
    	Doit(x);
    	for(int i = head[x] ,d ; i ; i = h[i].next){
    		if(vis[d = h[i].node]) continue;
    		mxsz[R = 0] = TreeSize = Size[d];
    		GetRoot(d ,x);
    		DivAndCon(R);
    	}
    }
    
    int main(){
    
    	__FILE([CTSC2018]暴力写挂);
    	int n = read();
    	for(int i = 1 ,u ,v ,w ; i < n ; ++i){
    		u = read() ,v = read() ,w = read();
    		addedge(u ,v ,w ,T1);
    	}
    	for(int i = 1 ,u ,v ,w ; i < n ; ++i){
    		u = read() ,v = read() ,w = read();
    		addedge(u ,v ,w ,T2);
    	}
    
    	// GetFA();
    
    	FOR_LCA::Main();
    	GetOrder(1 ,0);
    	GetDepth(1 ,0);
    
    	TreeSize = mxsz[R = 0] = n;
    	GetRoot(1 ,0) ,DivAndCon(R);
    	for(int i = 1 ; i <= n ; ++i){
    		Ans = std::max(Ans ,2 * (dep[i] - depT2[i]));
    	}
    	printf("%lld
    " ,Ans / 2);
    	return 0;
    }
    
  • 相关阅读:
    1094. Car Pooling
    121. Best Time to Buy and Sell Stock
    58. Length of Last Word
    510. Inorder Successor in BST II
    198. House Robber
    57. Insert Interval
    15. 3Sum java solutions
    79. Word Search java solutions
    80. Remove Duplicates from Sorted Array II java solutions
    34. Search for a Range java solutions
  • 原文地址:https://www.cnblogs.com/imakf/p/13763805.html
Copyright © 2011-2022 走看看