[JZOJ3329] 树上的路径

Description

给定一棵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}。

Solution

首先对这棵树点分治

如果对于每个分治中心的所有儿子都按顺序一个个插入,再排序,显然是不行的。
考虑把这个分治中心全部跑完排序完再放到一起做。

将这个分治中心的子树中的点按照DFS序排起来
可以发现对于一个点,能构成通过分治中心的路径的点(即不跟他在同一棵子树)体现在DFS序上就是左右两个区间。

那么用RMQ做出区间最大值,对于每个点加入大根堆,每次取堆顶加入维护答案的set,然后对顶的区间裂成两半区间,相应维护插回堆里即可。
复杂度 O(Nlog2N) O ( N l o g 2 N )

Code

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
#include <set>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 100005
#define M 300005
#define LL long long
using namespace std;
int pr[2*N],cnt,fs[N],nt[2*N],dt[2*N],sz[N],n,m1,m,mi,mw,n1,lim,ans[M],d[M],dis[N],dfn[N],rmq[N][16],cf[16],l2[N],fr[N],dfw[N];
bool bz[N];
multiset<int> h;
bool cmp(int x,int y)
{
    return x>y;
}
struct node
{
    int l,r,x,w,v;
    friend bool operator <(node x,node y)
    {
        return x.v<y.v;
    }
};
priority_queue<node> h1;
void link(int x,int y,int z)
{
    nt[++m1]=fs[x];
    dt[fs[x]=m1]=y;
    pr[m1]=z;
}
void find(int k,int fa,int num)
{
    sz[k]=1;
    int ms=0;
    for(int i=fs[k];i;i=nt[i])
    {
        int p=dt[i];
        if(p!=fa&&!bz[p])
        {
            find(p,k,num);
            sz[k]+=sz[p];
            ms=max(ms,sz[p]);
        }
    }
    if(max(num-sz[k],ms)<mi) mi=max(num-sz[k],ms),mw=k;
}
void dfs(int k,int fa,int s)
{
    if(fa==0) fr[k]=0;
    else if(fr[fa]==0) fr[k]=k;
    else fr[k]=fr[fa];
    dis[k]=s;
    dfn[dfw[k]=++dfn[0]]=k;
    sz[k]=1;
    for(int i=fs[k];i;i=nt[i])
    {
        int p=dt[i];
        if(p!=fa&&!bz[p]) 
        {
            dfs(p,k,s+pr[i]);
            sz[k]+=sz[p];
        }
    }
}
int get(int l,int r)
{
    int c=l2[r-l+1];
    return ((dis[dfn[rmq[l][c]]]>dis[dfn[rmq[r-cf[c]+1][c]]])?rmq[l][c]:rmq[r-cf[c]+1][c]);
}
void doit(int k)
{
    bz[k]=1;
    int id=0;
    dfn[0]=0;
    dfs(k,0,0);
    fo(i,1,dfn[0]) rmq[i][0]=i;
    fo(j,1,15) 
    {
        fo(i,1,dfn[0])
        {
            rmq[i][j]=(dis[dfn[rmq[i][j-1]]]>dis[dfn[rmq[i+cf[j-1]][j-1]]])?rmq[i][j-1]:rmq[i+cf[j-1]][j-1];
        }
    }
    while(!h1.empty()) h1.pop();
    int vc=0;
    fo(i,2,dfn[0])
    {
        node p1;
        p1.l=1,p1.r=dfw[fr[dfn[i]]]-1;
        p1.x=dfn[i];
        if(p1.l<=p1.r)
        {
            p1.w=get(p1.l,p1.r);
            p1.v=dis[dfn[i]]+dis[dfn[p1.w]];
            h1.push(p1);
            vc++;
        }
    }
    if(!h1.empty())
    {
    node px=h1.top();
    while(cnt<m||px.v>(*h.begin()))
    {
        if(cnt==m) h.erase(h.begin());
        else cnt++;
        h.insert(px.v);
        h1.pop();
        vc--;

        node p1=px,p2=px;
        p1.r=px.w-1;
        p2.l=px.w+1;
        if(p1.l<=p1.r)
        {
            p1.w=get(p1.l,p1.r);
            p1.v=dis[p1.x]+dis[dfn[p1.w]];
            h1.push(p1);
            vc++;
        }
        if(p2.l<=p2.r)
        {
            p2.w=get(p2.l,p2.r);
            p2.v=dis[p2.x]+dis[dfn[p2.w]];
            h1.push(p2);
            vc++;
        }
        if(h1.empty()) break;
        px=h1.top();
    }
    }
    for(int i=fs[k];i;i=nt[i])
    {
        int p=dt[i];
        if(!bz[p])
        {
            mi=n+1;
            find(p,k,sz[p]);    
            doit(mw);
        }
    }
}
int main()
{
    cin>>n>>m;
    cf[0]=1;
    fo(i,1,15) cf[i]=cf[i-1]*2,l2[cf[i]]=i;
    fo(i,1,n) if(!l2[i]) l2[i]=l2[i-1];
    fo(i,1,n-1)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        link(x,y,z),link(y,x,z);
    }
    mi=n+1;
    find(1,0,n);
    doit(mw);
    for(multiset<int>::iterator it=h.begin();it!=h.end();it++) ans[++ans[0]]=*it;
    fod(i,ans[0],1) printf("%d\n",ans[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值