#1490. 树上的路径

题意

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

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

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

题解

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

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

寻找最大值可用 s t st st 表实现

#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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值