bzoj2870: 最长道路tree

本文介绍了求解最长道路问题的一种方法,通过从大到小依次加入点,利用并查集维护联通性及直径信息。在合并过程中,找到新的直径端点,计算距离时可以采用倍增算法。此外,文章还提到了边分治的思路,详细阐述了如何进行边分治操作,包括边分治的重建过程,并给出了并查集和边分治的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题
这是一道权限题


一开始就想到可以从大到小加点,这样路径最小值就确定是 V x V_x Vx
然后我想到是用LCT维护,然后被题解吊锤了

讲一下如何维护,用并查集维护联通性,记录这个联通块的直径,和直径的两个端点

对于合并,你惊讶的发现两个联通块最终的直径的端点一定在四个原端点之间,可以直接枚举,取距离最大的一对点

那距离怎么算呢?你发现两点之间距离就是最开始树上的距离,这个想怎么算怎么算,我用的倍增

讲一下边分治:

假设你会边分治

首先防被卡先写一个边分重建,然后虚边为0,实变为1

因为一条边只分成两块,可以将两个子树的路径存进两个数组s,按照最小值排序

然后对于 s [ 0 ] [ i ] s[0][i] s[0][i] 找出第一个大于等于他的 s [ 1 ] [ j ] s[1][j] s[1][j],显然最小值就是 s [ 0 ] [ i ] s[0][i ] s[0][i], 路径长度就是 j − n j-n jn最长路径+当前 i i i的路径长度,对于左右各做一遍即可


并查集倍增

#include <cstdio>
#include <algorithm>
using namespace std;

#define dd c=getchar()
int read() {int s=0,w=1;char c;while (dd,c>'9' || c<'0') if (c=='-') w=-1;while (c>='0' && c<='9') s=s*10+c-'0',dd;return s*w;}
#undef dd
void write(int x) {if (x<0) putchar('-'),x=-x;if (x>=10) write(x/10);putchar(x%10|'0');}
void wln(int x) {write(x);putchar('\n');}void wsp(int x) {write(x);putchar(' ');}

const int N = 3e5 + 7;

struct edge {
	int t,nxt;
}e[N<<1];
int n,cnt,ans;
int V[N],head[N],dep[N],fa[N][18],F[N],ln[N],s[N][2],id[N];

void add(int u, int t) {
	e[++cnt] = (edge) {t,head[u]};head[u] = cnt;
}

bool cmp(int a, int b) {return V[a] > V[b];}

void dfs(int u, int F) {
	dep[u] = dep[F] + 1; fa[u][0] = F;
	for (int i = 1; i <= 17; i++) fa[u][i] = fa[fa[u][i-1]][i-1];
	for (int i = head[u]; i; i = e[i].nxt) {
		int t = e[i].t;
		if (t == F) continue;
		dfs(t, u);
	}
}

int LCA(int a, int b) {
	if (dep[a] > dep[b]) swap(a, b);
	for (int i = 17; ~i; i--) if (dep[fa[b][i]] >= dep[a]) b = fa[b][i];
	if (a == b) return a;
	for (int i = 17; ~i; i--) if (fa[a][i] != fa[b][i]) a = fa[a][i], b = fa[b][i];
	return fa[a][0];
}

int getf(int x) {return x == F[x] ? x : F[x] = getf(F[x]);}

void merge(int x, int y) {
	int len = 0, a = 0, b = 0;
	int k[4] = {s[x][0], s[x][1],s[y][0],s[y][1]};
	for (int i = 0; i < 4; i++)
		for (int j = i+1; j < 4; j++) {
			int lca = LCA(k[i], k[j]);
			int l = dep[k[i]] + dep[k[j]] - 2 * dep[lca] + 1;
			if (l > len) len = l, a = k[i], b = k[j];
		}
	F[y] = x; ln[x] = len; s[x][0] = a; s[x][1] = b;
}

int main() {
	n = read();
	for (int i = 1; i <= n; i++) V[i] = read();
	for (int i = 1, u, v; i < n; i++) {
		u = read(); v = read();
		add(u, v); add(v, u);
	}
	dfs(1,0);
	for (int i = 1; i <= n; i++) {
		F[i] = s[i][0] = s[i][1] = id[i] = i;
		ln[i] = 1;
	}
	sort(id + 1, id + n + 1, cmp);
	for (int i = 1; i <= n; i++) {
		int x = id[i];
		for (int j = head[x]; j; j = e[j].nxt) {
			int t = e[j].t;
			if (getf(x) == getf(t) || V[t] < V[x]) continue;
			merge(getf(x), getf(t));
		}
		ans = max(ans, V[x] * ln[getf(x)]);
	}
	wln(ans);
}

边分的代码

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;

#define dd c=getchar()
int read() {int s=0,w=1;char c;while (dd,c>'9' || c<'0') if (c=='-') w=-1;while (c>='0' && c<='9') s=s*10+c-'0',dd;return s*w;}
#undef dd
void write(int x) {if (x<0) putchar('-'),x=-x;if (x>=10) write(x/10);putchar(x%10|'0');}
void wln(int x) {write(x);putchar('\n');}void wsp(int x) {write(x);putchar(' ');}

const int N = 2e5 + 7;//Ëı¶ÄÚ´æ 

struct edge {
	int t,nxt,w;
}e[N<<1];
int n,num,cnt,ans,rt,Mx;
int V[N],head[N],siz[N],vis[N],c[2],mx[2][N];
vector<int> son[N];

void add(int u, int t, int w) {
	e[++cnt] = (edge) {t, head[u], w}; head[u] = cnt;
	e[++cnt] = (edge) {u, head[t], w}; head[t] = cnt;
}

void dfs(int x, int fa) {
	for (int i = head[x]; i; i = e[i].nxt) {
		int t = e[i].t;
		if (t != fa) dfs(t, x), son[x].push_back(t); 
	}
}

void rebuild() {
	memset(head, 0, sizeof head); cnt = 1;
	for (int i = 1; i <= num; i++) {
		int sum = son[i].size();
		if (sum <= 2) {
			for (int j = 0; j < sum; j++) 
				add(i, son[i][j], 1);
		}
		else {
			int x = ++num, y = ++num;
			V[x] = V[y] = V[i];
			add(i, x, 0); add(i, y, 0);
			for (int j = 0; j < sum; j++)
				if (j&1) son[x].push_back(son[i][j]);
				else son[y].push_back(son[i][j]);
		}
		son[i].clear();
	}
}

void getrt(int x, int fa, int Sz) {
	siz[x] = 1;
	for (int i = head[x]; i; i = e[i].nxt) {
		int t = e[i].t;
		if (vis[i>>1] || t == fa) continue;
		getrt(t, x, Sz);
		siz[x] += siz[t];
		int now = max(Sz - siz[t], siz[t]);
		if (now < Mx) Mx = now, rt = i;
	}
}
struct node {
	int v,dep;
	bool operator < (node a) const {
		return v < a.v;
	}
}s[2][N];

void getdep(int x, int fa, int dep, int Mn, int id) {
	Mn = min(Mn, V[x]);
	if (x <= n) s[id][++c[id]] = (node) {Mn, dep}; 
	for (int i = head[x]; i; i = e[i].nxt) {
		int t = e[i].t;
		if (t == fa || vis[i>>1]) continue;
		getdep(t, x, dep + e[i].w, Mn, id);
	}
}

void divide(int x, int Siz) {
	Mx = 1e9; getrt(x, 0, Siz);
	if (Mx >= 1e9) return;
	vis[rt>>1] = 1;
	c[0] = c[1] = 0;
	getdep(e[rt].t, 0, 0, 1e9, 0);
	getdep(e[rt^1].t, 0, e[rt].w, 1e9, 1);
	sort(s[0]+1, s[0]+c[0]+1);
	sort(s[1]+1, s[1]+c[1]+1);
	mx[0][c[0]+1] = mx[1][c[1]+1] = 0;
	for (int i = c[0]; i; i--) mx[0][i] = max(s[0][i].dep, mx[0][i+1]);
	for (int i = c[1]; i; i--) mx[1][i] = max(s[1][i].dep, mx[1][i+1]);
	for (int i = 1, j = 1; i <= c[0]; i++) {
		while (j <= c[1] && s[1][j].v < s[0][i].v) j++;
		if (j <= c[1]) ans = max(ans, s[0][i].v * (s[0][i].dep + mx[1][j] + 1));
	}
	for (int i = 1, j = 1; i <= c[1]; i++) {
		while (j <= c[0] && s[0][j].v < s[1][i].v) j++;
		if (j <= c[0]) ans = max(ans, s[1][i].v * (s[1][i].dep + mx[0][j] + 1));
	}
	int nw = rt, S2 = Siz - siz[e[rt].t];
	divide(e[nw].t, siz[e[nw].t]);
	divide(e[nw^1].t, S2);
}
int main() {
	freopen("2870.in", "r", stdin);
	freopen("2870.out", "w", stdout);
	num = n = read();
	for (int i = 1; i <= n; i++) V[i] = read();
	for (int i = 1, u, v; i < n; i++) {
		u = read(); v = read();
		add(u, v, 1);
	}
	dfs(1,0); rebuild();
	divide(1, num);
	wln(ans);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值