【CTSC2018】暴力写挂(边分治,虚树)

22 篇文章 0 订阅
4 篇文章 0 订阅
博客主要讨论了如何解决一道算法竞赛题目,涉及树上的最长链问题。临时选手temporaryDO在比赛中遇到难题,尝试通过枚举点对并利用树链剖分求解,但由于数组越界导致程序错误。博主通过分析题目,提出利用边分治和虚树来优化算法,降低复杂度。文章提供了详细的解题思路和代码实现,涉及数据结构如树链剖分、最近公共祖先等概念,并给出了样例输入和输出。
摘要由CSDN通过智能技术生成

题面

🔗

temporaryDO 是一个很菜的 OIer。在 4 月,他在省队选拔赛的考场上见到了《林克卡特树》一题,其中 k = 0 k = 0 k=0 的部分分是求树 T T T 上的最长链。可怜的 temporaryDO 并不会做这道题,他在考场上抓猫耳挠猫腮都想不出一点思路。

这时,善良的板板出现在了空中,他的身上发出璀璨却柔和的光芒,荡漾在考场上。“题目并不难。” 板板说。那充满磁性的声音,让 temporaryDO 全身充满了力量。

他决定:写一个枚举点对求 LCA 算距离的 k = 0 k = 0 k=0 O ( n 2 log ⁡ n ) O(n^2 \log n) O(n2logn) 的部分分程序!于是, temporaryDO 选择以 1 1 1 为根,建立了求 LCA 的树链剖分结构,然后写了二重 for 循环枚举点对。

然而,菜菜的 temporaryDO 不小心开小了数组,于是数组越界到了一片神秘的内存区域。但恰好的是,那片内存区域存储的区域恰好是另一棵树 T ′ T' T 。这样一来,程序并没有 RE ,但他求 x x x y y y 的距离的时候,计算的是
d e p t h ( x ) + d e p t h ( y ) − ( d e p t h ( L C A ( x , y ) ) + d e p t h ′ ( L C A ′ ( x , y ) ) ) \mathrm{depth}(x) + \mathrm{depth}(y) - ({\mathrm{depth}(\mathrm{LCA}(x,y))}+{\mathrm{depth'}(\mathrm{LCA'}(x,y))}) depth(x)+depth(y)(depth(LCA(x,y))+depth(LCA(x,y)))

最后程序会输出每一对点对 i , j i, j i,j i ≤ j i \le j ij) 的如上定义的“距离” 的最大值。temporaryDO 的程序在评测时光荣地爆零了。但他并不服气,他决定花好几天把自己的程序跑出来。请你根据 T T T T ′ T' T 帮帮可怜的 temporaryDO 求出他程序的输出。

输入格式

第一行包含一个整数 n n n ,表示树上的节点个数。

2 2 2 到第 n n n 行,每行三个整数 x , y , v x , y , v x,y,v ,表示 T T T 中存在一条从 x x x y y y 的边,其长度为 v v v

n + 1 n + 1 n+1 到第 2 n − 1 2n-1 2n1 行 ,每行三个整数 x , y , v x , y , v x,y,v ,表示 T ′ T' T 中存在一条从 x x x y y y 的边,其长度为 v v v

输出格式

输出一行一个整数,表示 temporaryDO 的程序的输出。

样例

输入

6
1 2 2
1 3 0
2 4 1
2 5 -7
3 6 0
1 2 -1
2 3 -1
2 5 3
2 6 -2
3 4 8

输出

5

数据范围

对于所有数据, 1 ≤ n ≤ 366666 1\le n \le 366666 1n366666 ∣ v ∣ ≤ 2017011328 |v| \le 2017011328 v2017011328 。 详细数据范围见下表,表格中的“无” 表示无特殊限制。

测试点编号 n ≤ n \le n v v v T T T 是一条链 T ′ T' T 是一条链
1 1 1 36 36 36 = 1 =1 =1
2 2 2 366 366 366 = 1 =1 =1
3 3 3 1388 1388 1388 > 0 >0 >0
4 4 4 1999 1999 1999 > 0 >0 >0
5 5 5 2666 2666 2666 > 0 >0 >0
6 6 6 5666 5666 5666
7 7 7 8666 8666 8666
8 8 8 11111 11111 11111
9 9 9 12345 12345 12345
10 10 10 366666 366666 366666 > 0 >0 >0
11 11 11 366666 366666 366666
12 ∼ 13 12\sim 13 1213 366666 366666 366666 > 0 >0 >0
14 14 14 366666 366666 366666
15 ∼ 16 15\sim 16 1516 366666 366666 366666 > 0 >0 >0
17 17 17 366666 366666 366666
18 ∼ 20 18\sim 20 1820 366666 366666 366666

d e p t h ( p ) \mathrm{depth}(p) depth(p) d e p t h ′ ( p ) \mathrm{depth'}(p) depth(p) 分别表示树 T T T T ′ T' T 中点 1 1 1 到点 p p p 的距离,这里规定,距离指的是经过的边的边权总和,其中 d e p t h ( 1 ) = 0 \mathrm{depth}(1) = 0 depth(1)=0

L C A ( x , y ) \mathrm{LCA}(x, y) LCA(x,y) L C A ′ ( x , y ) \mathrm{LCA'}(x, y) LCA(x,y) 分别表示树 T T T T ′ T' T 中点 x x x 与点 y y y 的最近公共祖先,即在从 x x x y y y 的最短路径上的距离根经过边数最少的点。

题解

我们令 d i s ( x , y ) \mathrm{dis}(x,y) dis(x,y) T T T x , y x,y x,y 两点的距离(路径上的边权和),那么答案等价于
d e p t h ( x ) + d e p t h ( y ) + d i s ( L C A ( x , y ) ) − d e p t h ′ ( L C A ′ ( x , y ) ) 2 \cfrac{\mathrm{depth}(x) + \mathrm{depth}(y) + {\mathrm{dis}(\mathrm{LCA}(x,y))}-{\mathrm{depth'}(\mathrm{LCA'}(x,y))}}{2} 2depth(x)+depth(y)+dis(LCA(x,y))depth(LCA(x,y))

那么我们可以对 T T T 进行边分治,记录每个点到分治中心的距离,转化成点权,左部和右部其中一边加上分治中心的边权,然后在 T ′ T' T 上建立该连通子树的虚树,求每个点作为 L C A LCA LCA 时两个不同部后代点权和的最大值。

建立虚树的复杂度或常数要尽量小,若建立虚树的复杂度为 T ( n ) T(n) T(n) ,则算法时间复杂度为 O ( T ( n ) log ⁡ n ) O(T(n)\log n) O(T(n)logn)

在这里插入图片描述

注: x = y x=y x=y 时的贡献是 d e p t h ( x ) − d e p t h ′ ( x ) {\rm depth}(x)-{\rm depth'}(x) depth(x)depth(x)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

CODE

一个函数实现三度化,虚树复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn) ,重链剖分常数。

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<random>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 736670
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB long double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
int xchar() {
	static const int maxn = 1000000;
	static char b[maxn];
	static int pos = 0,len = 0;
	if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
	if(pos == len) return -1;
	return b[pos ++];
}
#define getchar() xchar()
LL read() {
	LL f = 1,x = 0;int s = getchar();
	while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
	while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
	return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {
	if(!x) {putchar('0');return ;}
	if(x<0) putchar('-'),x = -x;
	return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}

int n,m,s,o,k;
int hd[MAXN],nx[MAXN<<3],v[MAXN<<3],cne;
LL w[MAXN<<3];
int hd2[MAXN],hd3[MAXN];
LL de[MAXN];
int d[MAXN],fa[MAXN],sz[MAXN],sn[MAXN],tp[MAXN],dfn[MAXN],tim;
bool cmp(int a,int b) {return dfn[a] < dfn[b];}
LL dept[MAXN];
bool f[MAXN],tg[MAXN];
LL dp[MAXN],elen;
int siz[MAXN],SIZ,el,er,ls,rs;
int op[MAXN];
int ar[MAXN],cna;
LL ans = -1e18;
LL nm1[MAXN],nm2[MAXN];
void ins1(int x,int y,LL z) {
	nx[++ cne] = hd[x]; v[cne] = y; hd[x] = cne; w[cne] = z;
}
void ins2(int x,int y,LL z) {
	nx[++ cne] = hd2[x]; v[cne] = y; hd2[x] = cne; w[cne] = z;
}
void ins(int cne,int x) {nx[cne] = hd[x]; hd[x] = cne;}
void ins3(int x,int y,LL z) {
	nx[++ cne] = hd3[x]; v[cne] = y; hd3[x] = cne; w[cne] = z;
}
void dfs0(int x,int ff) {
	d[x] = d[fa[x] = ff] + 1;
	sz[x] = 1; sn[x] = 0;
	for(int i = hd2[x];i;i = nx[i]) {
		if(v[i] != ff) {
			de[v[i]] = de[x] + w[i];
			dfs0(v[i],x);
			sz[x] += sz[v[i]];
			if(sz[v[i]] > sz[sn[x]]) sn[x] = v[i];
		}
	}return ;
}
void dfs0_(int x,int ff) {
	if(sn[ff] == x) tp[x] = tp[ff];
	else tp[x] = x;
	dfn[x] = ++ tim;
	if(sn[x]) dfs0_(sn[x],x);
	for(int i = hd2[x];i;i = nx[i]) {
		if(v[i] != ff && v[i] != sn[x]) {
			dfs0_(v[i],x);
		}
	}return ;
}
int lca(int a,int b) {
	while(tp[a] != tp[b]) {
		if(d[tp[a]] > d[tp[b]]) a = fa[tp[a]];
		else b = fa[tp[b]];
	}return d[a] < d[b] ? a:b;
}
void dfs00(int x,int ff) {
	int ct = 0; tg[x] = 1;
	for(int i = hd[x];i;i = nx[i]) {
		if(!tg[v[i]]) ct ++;
	}
	if(ct > 3) {
		int l = ++ m,r = ++ m,op = 0;
		for(int i = hd[x],j = nx[i];i;i = j,j = nx[i],op ^= 1) {
			if(tg[v[i]]) {op ^= 1; continue;}
			if(op) ins(i,r);
			else ins(i,l);
		}
		hd[x] = 0;
		ins1(x,l,0); ins1(x,r,0);
	}
	else {
		int hed = hd[x]; hd[x] = 0;
		for(int i = hed,j = nx[i];i;i = j,j = nx[i]) {
			if(tg[v[i]]) {continue;}
			ins(i,x);
		}
	}
	for(int i = hd[x];i;i = nx[i]) {
		if(!tg[v[i]]) {
			dept[v[i]] = dept[x] + w[i];
			dfs00(v[i],x);
			ins1(v[i],x,w[i]);
		}
	}
	return ;
}
void dfs(int x,int ff) {
	siz[x] = 1;
	for(int i = hd[x];i;i = nx[i]) {
		if(v[i] != ff && !f[v[i]]) {
			dfs(v[i],x);
			siz[x] += siz[v[i]];
			if(min(SIZ-siz[v[i]],siz[v[i]]) >= min(ls,rs)) {
				ls = SIZ-siz[v[i]]; rs = siz[v[i]];
				el = x; er = v[i]; elen = w[i];
			}
		}
	}return ;
}
void cont(int x,int ff,int o,LL wi) {
	if(x <= n) op[x] = o,ar[++ cna] = x,dp[x] = wi + dept[x];
	for(int i = hd[x];i;i = nx[i]) {
		if(v[i] != ff && !f[v[i]]) cont(v[i],x,o,wi + w[i]);
	}return ;
}
int build(int *a,int le) {
	sort(a + 1,a + 1 + le,cmp);
	static int st[MAXN],tp;
	st[tp = 1] = a[1]; hd3[a[1]] = 0;
	for(int i = 2;i <= le;i ++) {
		int p = 0,lc = lca(a[i],st[tp]);
		while(tp > 0 && d[lc] <= d[st[tp]]) {
			if(p) ins3(st[tp],p,de[p] - de[st[tp]]);
			p = st[tp --];
		}st[++ tp] = lc;
		if(p && p != lc) hd3[lc] = 0,ins3(lc,p,de[p] - de[lc]);
		if(a[i] != lc) hd3[a[i]] = 0,st[++ tp] = a[i];
	}
	int p = 0;
	while(tp > 0) {
		if(p) ins3(st[tp],p,de[p] - de[st[tp]]);
		p = st[tp --];
	}return p;
}
void calc(int x) {
	nm1[x] = -1e18; nm2[x] = -1e18;
	if(op[x] == 1) nm1[x] = dp[x];
	if(op[x] == 2) nm2[x] = dp[x];
	for(int i = hd3[x];i;i = nx[i]) {
		calc(v[i]);
		ans = max(ans,nm1[x] + nm2[v[i]] - de[x]*2);
		ans = max(ans,nm2[x] + nm1[v[i]] - de[x]*2);
		nm1[x] = max(nm1[x],nm1[v[i]]);
		nm2[x] = max(nm2[x],nm2[v[i]]);
	}op[x] = 0; hd3[x] = 0;
	return ;
}
void solve(int x,int si) {
	if(si == 1) {
		if(x <= n) ans = max(ans,dept[x]*2-de[x]*2);
		return ;
	}
	SIZ = si;
	ls = rs = 0;
	dfs(x,0);
	cna = 0;
	cont(el,er,1,elen); cont(er,el,2,0);
	int ce = cne;
	int rt = build(ar,cna),lsi = ls,rsi = rs,lp = el,rp = er;
	calc(rt);
	cne = ce;
	f[rp] = 1; solve(lp,lsi); f[rp] = 0;
	f[lp] = 1; solve(rp,rsi); f[lp] = 0;
	return ;
}
int main() {
	n = read(); m = n;
	for(int i = 1;i < n;i ++) {
		s = read();o = read();
		LL k = read();
		ins1(s,o,k); ins1(o,s,k);
	}
	for(int i = 1;i < n;i ++) {
		s = read();o = read();
		LL k = read();
		ins2(s,o,k); ins2(o,s,k);
	}
	dfs0(1,0);
	dfs0_(1,0);
	dfs00(1,0);
	solve(1,m);
	AIput(ans/2,'\n');
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值