题意
内存限制:256 MiB
时间限制:1000 ms
给定一棵 $N$ 个结点的树,结点用正整数 $1…N$ 编号,每条边有一个正整数权值。用 $d(a,b)$ 表示从结点 $a$ 到结点 $b$ 路径上经过边的权值和,其中要求 $a<b$ 。将这 $frac{N imes (N-1)}{2}$ 个距离值从大到小排序,输出前 $M$ 个距离值。
$N leq 50000$ , $M leq min(frac{N imes (N-1)}{2},300000)$
题解
考虑点分,建立出点分序,表示的时点分时经过的点,空间为 $O(n log n)$ ,每个点 $x$ 对应三元组 ${l,r,v}$ 表示 $x$ 点到重心的距离为 $v$ ,且可以取 $[l,r]$ 中的点组合形成一条链
然后对于每个点找到其最长链并且加入堆中,形成四元组 ${l,r,max_{l,r},x}$ ,然后取出堆中的元素,并且找到 $max_{l,r}$ 所在的位置 $p$ ,并且将 ${l,p-1,max_{l,p-1},x}$ 和 ${p+1,r,max_{p+1,r},x}$ 加入堆中,循环 $m$ 次即可
寻找最大值可用 $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 ",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; }