[BZOJ 3784] 树上的路径

7 篇文章 0 订阅
4 篇文章 0 订阅
BZOJ传送门

题目描述

给定一个 N N N个结点的树,结点用正整数 1.. N 1..N 1..N编号。每条边有一个正整数权值。用 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 n*(n-1)/2 n(n1)/2个距离从大到小排序,输出前 M M M个距离值。

输入输出格式

输入格式

第一行两个正整数 N , M N,M N,M

下面 N − 1 N-1 N1行,每行三个正整数 a , b , c ( a , b ≤ N , C ≤ 10000 ) a,b,c(a,b\le N,C\le 10000) a,b,c(a,bNC10000)。表示结点 a a a到结点 b b b有一条权值为 c c c的边。

输出格式

M M M行,如题所述.

输入输出样例

输入样例#1:
5 10 
1 2 1 
1 3 2 
2 4 3 
2 5 4 
输出样例#2:
7 
7 
6 
5 
4 
4 
3 
3 
2 
1 

数据范围

N ≤ 50000 , M ≤ m i n ( 300000 , n ∗ ( n − 1 ) 2 ) N\le 50000,M\le min(300000,\frac{n*(n-1)}{2}) N50000,Mmin(300000,2n(n1))

解题分析

第一眼看到所有权值为正且求 k k k大, 就想到超级钢琴这道题。

但是在树上无法直接这么搞, 需要转化一下, 膜了一波题解后知道了点分序列这种骚操作。

具体而言, 我们类似点分的方式来处理这些路径, 每次对于一个点分重心只考虑经过这个点的路径, 那么可以分成两部分: 一个点到达某棵子树的底部, 一个点在另一棵子树出发。

为了不重复统计, 直接利用 D F S DFS DFS序, 只考虑第二个点, 第一个点取在已经 D F S DFS DFS过的一段连续区间内, 可以直接用 S T ST ST表统计出这些点到根节点的距离。

这样操作之后问题也就变成了和超级钢琴一样的做法。

因为每个点出现了 l o g ( N ) log(N) log(N)次, 合法路径有 N l o g ( N ) Nlog(N) Nlog(N)条, 总复杂度为 O ( N l o g 2 ( N ) ) O(Nlog^2(N)) O(Nlog2(N))

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <queue>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
#define MX 300050
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
template <class T> IN T max(T a, T b) {return a > b ? a : b;}
template <class T> IN T min(T a, T b) {return a < b ? a : b;}
int n, m, cnt, tot, root, arr, st, ed, deal;
struct Seq
{
	int lef, rig, dis;
} que[MX * 20];
struct Edge {int to, len, nex;} edge[MX << 1];
struct INFO {int lef, rig, dis, tar;};
IN bool operator < (const INFO &x, const INFO &y)
{return x.dis + que[x.tar].dis < y.dis + que[y.tar].dis;}
std::priority_queue <INFO> pq;
int head[MX], dis[MX], mx[MX], siz[MX], ST[17][MX * 20], lg[MX * 20];
bool vis[MX];
IN void add(R int from, R int to, R int len) {edge[++cnt] = {to, len, head[from]}, head[from] = cnt;}
void DFS(R int now, R int fa)
{
	siz[now] = 1, mx[now] = 0;
	for (R int i = head[now]; i; i = edge[i].nex)
	{
		if (edge[i].to == fa || vis[edge[i].to]) continue;
		DFS(edge[i].to, now);
		siz[now] += siz[edge[i].to];
		if (siz[edge[i].to] > mx[now]) mx[now] = siz[edge[i].to];
	}
}
void findrt(R int now, R int fa)
{
	for (R int i = head[now]; i; i = edge[i].nex)
	{
		if (edge[i].to == fa || vis[edge[i].to]) continue;
		findrt(edge[i].to, now);
	}
	mx[now] = max(mx[now], deal - siz[now]);
	if (mx[now] < mx[root]) root = now;
}
void build(R int now, R int fa)
{
	++cnt;
	que[cnt] = {st, ed, dis[now]};
	for (R int i = head[now]; i; i = edge[i].nex)
	{
		if (edge[i].to == fa || vis[edge[i].to]) continue;
		dis[edge[i].to] = dis[now] + edge[i].len;
		build(edge[i].to, now);
	}
}
void work(R int now)
{
	st = ++cnt; dis[now] = 0;
	vis[now] = true;
	for (R int i = head[now]; i; i = edge[i].nex)
	{
		if (vis[edge[i].to]) continue;
		ed = cnt; dis[edge[i].to] = edge[i].len;
		build(edge[i].to, now);
	}
	for (R int i = head[now]; i; i = edge[i].nex)
	{
		if (vis[edge[i].to]) continue;
		root = 0;
		DFS(edge[i].to, now); deal = siz[edge[i].to];
		findrt(edge[i].to, now);
		work(root);
	}
}
IN int Max(R int x, R int y)
{return que[x].dis > que[y].dis ? x : y;}
void ST_init()
{
	for (R int i = 2; i <= cnt; ++i) lg[i] = lg[i >> 1] + 1;
	for (R int i = 1; i <= cnt; ++i) ST[0][i] = i;
	for (R int i = 1; i <= 17; ++i)
	{
		int step = 1 << (i - 1), bd = cnt - (1 << i) + 1;
		for (R int j = 1; j <= bd; ++j)
		ST[i][j] = Max(ST[i - 1][j], ST[i - 1][j + step]);
	}
}
int query(R int lef, R int rig)
{
	int tar = lg[rig - lef + 1];
	return Max(ST[tar][lef], ST[tar][rig - (1 << tar) + 1]);
}
int main(void)
{
	in(n), in(m); int a, b, c;
	for (R int i = 1; i < n; ++i)
	{
		in(a), in(b), in(c);
		add(a, b, c), add(b, a, c);
	}
	mx[0] = 100000000;
	work(1); ST_init(); INFO foo;
	for (R int i = 1; i <= cnt; ++i)
	if (que[i].lef) pq.push({que[i].lef, que[i].rig, que[i].dis, query(que[i].lef, que[i].rig)});
	W (m--)
	{
		foo = pq.top(), pq.pop();
		printf("%d\n", foo.dis + que[foo.tar].dis);
		if (foo.tar != foo.lef) pq.push({foo.lef, foo.tar - 1, foo.dis, query(foo.lef, foo.tar - 1)});
		if (foo.tar != foo.rig) pq.push({foo.tar + 1, foo.rig, foo.dis, query(foo.tar + 1, foo.rig)});
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值