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 < b a<b a<b.将这 n ∗ ( n − 1 ) / 2 n*(n-1)/2 n∗(n−1)/2个距离从大到小排序,输出前 M M M个距离值。
输入输出格式
输入格式
第一行两个正整数 N , M N,M N,M
下面 N − 1 N-1 N−1行,每行三个正整数 a , b , c ( a , b ≤ N , C ≤ 10000 ) a,b,c(a,b\le N,C\le 10000) a,b,c(a,b≤N,C≤10000)。表示结点 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}) N≤50000,M≤min(300000,2n∗(n−1))
解题分析
第一眼看到所有权值为正且求 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)});
}
}