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;
    }
    
  • 相关阅读:
    shell与export命令
    mysql同步出现1062错误
    mysql命令行执行时不输出列名(字段名)
    python中中括号中的负数
    bash: ssh: command not found
    nagios的一些东西
    安装MySQLdb出现HAVE_WCSCOLL重定义问题的解决方法
    ImportError: No module named setuptools
    xp密钥
    破解MySQL和修改mysql的密码
  • 原文地址:https://www.cnblogs.com/imakf/p/13763805.html
Copyright © 2011-2022 走看看