#1490. 树上的路径

题意

内存限制:256 MiB
时间限制:1000 ms

给定一棵 NNN 个结点的树,结点用正整数 1…N1…N1N 编号,每条边有一个正整数权值。用 d(a,b)d(a,b)d(a,b) 表示从结点 aaa 到结点 bbb 路径上经过边的权值和,其中要求 a&lt;ba&lt;ba<b 。将这 N×(N−1)2\frac{N \times (N-1)}{2}2N×(N1) 个距离值从大到小排序,输出前 MMM 个距离值。

N≤50000N \leq 50000N50000M≤min(N×(N−1)2,300000)M \leq min(\frac{N \times (N-1)}{2},300000)Mmin(2N×(N1),300000)

题解

考虑点分,建立出点分序,表示的时点分时经过的点,空间为 O(nlog⁡n)O(n \log n)O(nlogn) ,每个点 xxx 对应三元组 l,r,v{l,r,v}l,r,v 表示 xxx 点到重心的距离为 vvv ,且可以取 [l,r][l,r][l,r] 中的点组合形成一条链

然后对于每个点找到其最长链并且加入堆中,形成四元组 l,r,maxl,r,x{l,r,max_{l,r},x}l,r,maxl,r,x ,然后取出堆中的元素,并且找到 maxl,rmax_{l,r}maxl,r 所在的位置 ppp ,并且将 l,p−1,maxl,p−1,x{l,p-1,max_{l,p-1},x}l,p1,maxl,p1,xp+1,r,maxp+1,r,x{p+1,r,max_{p+1,r},x}p+1,r,maxp+1,r,x 加入堆中,循环 mmm 次即可

寻找最大值可用 ststst 表实现

#include <bits/stdc++.h>
#define I inline
using namespace std;
const int N=5e4+5;bool vis[N];
int n,m,sz[N],son[N],rt,o,hd[N],V[N*2];
int W[N*2],nx[N*2],t,f[N*16][21],L,R,Lg[N*16];
struct O{int l,r,v;}p[N*16];
struct Q{
	int l,r,x,y;
	I friend bool operator < (Q A,Q B){
		return p[A.x].v+p[A.y].v<p[B.x].v+p[B.y].v;
	}
};
priority_queue<Q>q;
I void add(int u,int v,int w){
	nx[++t]=hd[u];V[hd[u]=t]=v;W[t]=w;
}
#define v V[i]
I void getrt(int x,int fa){
	sz[x]=1;son[x]=0;
	for (int i=hd[x];i;i=nx[i])
		if (v!=fa && !vis[v])
			getrt(v,x),sz[x]+=sz[v],
			son[x]=max(son[x],sz[v]);
	son[x]=max(o-sz[x],son[x]);
	if (son[x]<son[rt]) rt=x;
}
I void dfs(int x,int fa,int w){
	p[++t]=(O){L,R,w};f[t][0]=t;
	for (int i=hd[x];i;i=nx[i])
		if (v!=fa && !vis[v])
			dfs(v,x,w+W[i]);
}
I void work(int x){
	L=R=++t;f[t][0]=t;
	p[t]=(O){t,t,0};vis[x]=1;
	for (int i=hd[x];i;i=nx[i])
		if (!vis[v]) dfs(v,x,W[i]),R=t;
	for (int i=hd[x];i;i=nx[i])
		if (!vis[v])
			rt=0,o=sz[v],getrt(v,x),work(rt);
}
#undef v
I int ax(int x,int y){
	return p[x].v>p[y].v?x:y;
}
I int query(int l,int r){
	int i=Lg[r-l+1];
	return ax(f[l][i],f[r-(1<<i)+1][i]);
}
I void push(int l,int r,int x){
	q.push((Q){l,r,x,query(l,r)});
}
int main(){
	son[0]=1e9;
	scanf("%d%d",&n,&m);
	for (int x,y,z,i=1;i<n;i++)
		scanf("%d%d%d",&x,&y,&z),
		add(x,y,z),add(y,x,z);
	t=0;o=n;getrt(1,0);work(rt);
	for (int i=2;i<=t;i++) Lg[i]=Lg[i>>1]+1;
	for (int i=t;i;i--)
		for (int j=1;i+(1<<j)<=t+1;j++)
			f[i][j]=ax(f[i][j-1],f[i+(1<<(j-1))][j-1]);
	for (int i=1;i<=t;i++) push(p[i].l,p[i].r,i);
	while(m--){
		Q k=q.top();q.pop();
		printf("%d\n",p[k.x].v+p[k.y].v);
		if (k.y>k.l) push(k.l,k.y-1,k.x);
		if (k.y<k.r) push(k.y+1,k.r,k.x);
	}
	return 0;
}
### 关于结构中求解最大得分和路径的问题 在解决结构中的最大得分路径问题时,通常会涉及到深度优先搜索 (DFS) 或者广度优先搜索 (BFS),具体取决于问题的需求。对于最大化路径得分这类问题,动态规划的思想也常常被应用其中。 #### 动态规划与深度优先搜索相结合的方法 一种常见的方式是从根节点出发,利用递归来遍历整棵,并记录下到达每一个子节点的最大得分。这种方法可以有效地避免重复计算,从而提高效率[^1]。 ```python def max_score_path(root, scores): """ 计算从root到叶子结点的最大得分路径 参数: root: 的根节点 scores: 各个节点对应的分数列 返回: tuple: 最大得分为第一个元素,最佳路径为第二个元素组成的元组 """ if not root.left and not root.right: return scores[root.val], [scores[root.val]] left_max = (-float(&#39;inf&#39;), []) right_max = (-float(&#39;inf&#39;), []) if root.left: left_max = max_score_path(root.left, scores) if root.right: right_max = max_score_path(root.right, scores) best_child = max(left_max, right_max, key=lambda x:x[0]) current_best = (best_child[0]+scores[root.val], [scores[root.val]] + best_child[1]) return current_best ``` 此函数`max_score_path()`接收两个参数:一个示当前正在访问的节点的对象 `root` ,另一个一个包含各个节点对应分数的列`scores` 。它返回的是一个由两部分构成的元组——第一项代这条路径上的总分值;第二项则是一条具体的路径序列。 当遇到叶节点时(即没有左孩子也没有右孩子的节点),直接将其自身的分数作为初始条件返回。而对于非叶节点,则分别对其左右子调用相同的逻辑来获取可能的最佳路径,并最终选取两者之中更优的那个加上自己本身的分数形成新的候选方案。 #### 使用贪心策略简化特定情况下的解决方案 如果题目设定允许某些特殊假设成立的话,比如每一步都只考虑局部最优解而不会影响全局结果的情况下,那么也可以尝试采用更加简单的贪心算法来进行快速估算。不过需要注意的是,在大多数情况下这种做法并不能保证一定能找到真正的全局最优点[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值