#1490. 树上的路径

题意
内存限制: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;
}
原文地址:https://www.cnblogs.com/xjqxjq/p/10685667.html