题目喵述
题目大意:给一棵树,有N个点,有边权。所有无向路径中,请你输出前M大的。
N<=50000
M<=Min(300000,n*(n-1) /2)
思路
树上超级钢琴。(拖延症的结果就是现在才写这题)
学了一种点分治的套路,在做点分治的时候求出整棵树的dfs序。可以发现,每个点在dfs序列中出现不超过log次。我们不妨称其为点分治序。
然后类比超级钢琴,将序列转换到树上。强制路径过分治中心,然后每一条路径的长度对应Dis[x]+Dis[y]。其中x和y要求在分治中心的不同子树内。这里我们不可以直接做然后减什么的,有一种方便的技巧就是枚举儿子,每个儿子dfs一次。记录每个点为路径尾能取到的头的区间[L,R]。
接下来,直接在点分治序上做ST表,丢进大根堆里然后分裂区间即可,这里跟在序列上几乎一样。
时间复杂度是两个log的。
还有一种二分的做法,加上每次点分治总共有3个log,是过不了的,但是先点分治一次,排好序并记下来,然后就只有两个log了,也是很强的。
代码
#include <bits/stdc++.h>
#define maxn 800010
#define Lg 20
using namespace std;
int n, m, cur = -1;
struct List{
List *next;
int obj, len;
}*head[maxn], Edg[maxn<<1];
void Addedge(int a, int b, int c){
Edg[++cur].next = head[a];
Edg[cur].obj = b;
Edg[cur].len = c;
head[a] = Edg+cur;
}
bool Vis[maxn];
int Root, Sum, Dfn;
int son[maxn], siz[maxn], fa[maxn];
int T[maxn], f[Lg][maxn], g[Lg][maxn];
int tL[maxn], tR[maxn], Dis[maxn];
struct World{
int x, y, l, r;
World() {}
World(