题意
给定一棵N个结点的树,结点用正整数1..N编号,每条边有一个正整数权值。用d(a,b)表示从结点a到结点b路径上经过边的权值和,其中要求 a<b a < b 。将这N*(N-1)/2个距离值从大到小排序,输出前M个距离值。
对于100%的数据满足N<=50,000,M<=min{N*(N-1)/2,300,000}。
+O2
题解
有两种解法。
第一种非常经典,二分第M大在哪,然后去统计个数。
统计个数也很简单,裸的点分治就行。
但是这样二分 + 排序 + 点分治 是三个log,妥妥的TLE
其实可以先排好序提出来,后面不需要再进点分治和排序。这样复杂度可以减掉一个log
用vector比较方便。
第二种解法也是经典套路
求第K大,那么我们发现,对于一个确定的分治重心,每个点与他能成被统计路径的点 是之前的子树。这样我们把所谓的点分治序列建出来,就相当于在这几个子树区间选最大的放进堆里,取出后再分为两个最大值候选区间放进堆里。这样从堆中取m次。
然后最大值用倍增表(不用好像也没啥问题)
这样是两个log的,但理论复杂度会比第一种慢(log (n log n)),但是常数小了很多,实际要快。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int N = 5e4 + 10;
int n,m;
int final[N],nex[2 * N],to[2 * N],tot,e[2 * N],size[N];
int stm,w[16 * N],L,R;
int vis[N],allsz,hvy,hmx;
struct node{
int l,r,x,mx;
friend bool operator < (const node &a, const node &b) {
return w[a.x] + w[a.mx] < w[b.x] + w[b.mx];
}
};
priority_queue<node> que;
void link(int x,int y,int c) {
to[++tot] = y;
nex[tot] = final[x];
final[x] = tot;
e[tot] = c;
}
void init(int x,int fa) {
size[x] = 1;
for (int i=final[x]; i; i=nex[i]) {
int y = to[i];
if (y != fa && !vis[y]) {
init(y,x);
size[x] += size[y];
}
}
}
void gethvy(int x,int fa) {
int mx = 0;
for (int i=final[x]; i; i=nex[i]) {
int y = to[i];
if (y != fa && !vis[y]) {
gethvy(y,x);
mx = max(mx, size[y]);
}
}
mx = max(mx,allsz - size[x]);
if (hvy == 0 || hmx > mx) hvy = x, hmx = mx;
}
int pre,gre;
void dfs(int x,int fa,int dis) {
w[++stm] = dis;
que.push((node){L,R,stm,pre});
if (w[gre] < w[stm]) gre = stm;
for (int i=final[x]; i; i=nex[i]) {
int y = to[i];
if (y != fa && !vis[y]) {
dfs(y,x,dis+e[i]);
}
}
}
void solve(int root) {
hvy = 0;
init(root,0), allsz = size[root];
gethvy(root,0);
w[L = R = ++stm] = 0;
pre = gre = L;
for (int i=final[hvy]; i; i=nex[i]) {
int y = to[i];
if (!vis[y]) {
dfs(y, hvy, e[i]);
R = stm;
pre = gre;
}
}
vis[hvy] = 1;
for (int i=final[hvy]; i; i=nex[i]) {
int y = to[i]; if (!vis[y]) {
solve(y);
}
}
}
int g[16 * N][20];
int mi[20],log[16 * N];
node top;
int fmax(int l,int r) {
int sz = r - l + 1;
int z = log[sz] - 1;
return w[g[l][z]] > w[g[r - (1<<z) + 1][z]] ? g[l][z] : g[r - (1<<z) + 1][z];
}
int main() {
freopen("tp.in","r",stdin);
cin>>n>>m;
for (int i=0; i<=20; i++) mi[i] = 1<<i;
for (int i=1; i<n; i++) {
int a,b,c;scanf("%d %d %d",&a,&b,&c);
link(a,b,c), link(b,a,c);
}
solve(1);
for (int i=1; i<=stm; i++) log[i] = log[i>>1] + 1;
for (int i=stm; i; i--) {
g[i][0] = i;
for (int j=1; j<20; j++) {
int nx = i + mi[j-1];
if (nx <= stm)
g[i][j] = (w[g[i][j-1]] > w[g[nx][j-1]]) ? g[i][j-1] : g[nx][j-1];
else
g[i][j] = g[i][j-1];
}
}
for (int z=1; z<=m; z++) {
top = que.top(); que.pop();
printf("%d\n",w[top.x] + w[top.mx]);
if (top.l < top.mx)
que.push((node) {top.l,top.mx-1,top.x,fmax(top.l,top.mx-1) });
if (top.mx < top.r)
que.push((node) {top.mx+1,top.r,top.x,fmax(top.mx+1,top.r) });
}
}