【bzoj3784】【树上的路径】【点分治+堆+st表】

Description

给定一个N个结点的树,结点用正整数1..N编号。每条边有一个正整数权值。用d(a,b)表示从结点a到结点b路边上经过边的权值。其中要求a<b.将这n*(n-1)/2个距离从大到小排序,输出前M个距离值。

Input

第一行两个正整数N,M
下面N-1行,每行三个正整数a,b,c(a,b<=N,C<=10000)。表示结点a到结点b有一条权值为c的边。

Output

共M行,如题所述.

Sample Input

5 10
1 2 1
1 3 2
2 4 3
2 5 4

Sample Output

7
7
6
5
4
4
3
3
2
1

HINT

N<=50000,M<=Min(300000,n*(n-1) /2 )

题解:
          其实就是超级钢琴那个题扩展到树上.
          点分治过程中把访问到的点记下来可以得到一个点分治序列并记录他们的dis值.
          这个序列中的每个点x都可以求出一个[l,r],表示点x可以与序列中[l,r]的点形成链.
          对于每个点都可以维护一个4元组{l,r,h,mx}
          分别表示这个点在点分治序列中的编号是h,可以与序列中[l,r]的点形成链,[l,r]中dis的最大值在mx位置.
          这样这个点所形成链的最大值就是xu[h]+xu[mx],xu表示点分治序列并且存的是dis值.
          用一个堆来维护这些4元组.
          取出一个四元组{l,r,h,mx}之后将这个4元组裂解成{l,mx-1.h,max(l,mx-1)},{mx+1,r,h,max(mx+1,r)}再加入堆.
          询问区间最值可以用st表实现.
          取M次堆顶即可.
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<cmath>
#define N 50010
#define M 300010
int h[N],n,m,root,size[N],sum,vis[N],cnt,next[N<<1],point[N],dis[N],x,y,v;
int l,r,xu[N*20],id,f[20][N*20];
struct edge{int st,en,v;}e[N<<1];
struct interval{int l,r;}p[N*20];
struct use{int l,r,h,mq;};
using namespace std;
priority_queue<use>q;
bool operator<(use a,use b){
  return xu[a.h]+xu[a.mq]<xu[b.h]+xu[b.mq];
} 
int read(){
  int x(0);char ch=getchar();
  while (ch<'0'||ch>'9') ch=getchar();
  while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
  return x;
}
void add(int x,int y,int v){
  next[++cnt]=point[x];point[x]=cnt;
  e[cnt].st=x;e[cnt].en=y;e[cnt].v=v;
}
void getroot(int x,int fa){
  size[x]=1;h[x]=0;
  for (int i=point[x];i;i=next[i])
    if (e[i].en!=fa&&!vis[e[i].en]){
      getroot(e[i].en,x);
      size[x]+=size[e[i].en];
      h[x]=max(h[x],size[e[i].en]); 
    }
  h[x]=max(h[x],sum-size[x]);
  if (h[x]<h[root]) root=x;
}
void getroute(int x,int fa){
  xu[++id]=dis[x];p[id]={l,r};
  for(int i=point[x];i;i=next[i])
    if (e[i].en!=fa&&(!vis[e[i].en])){
       dis[e[i].en]=dis[x]+e[i].v;
       getroute(e[i].en,x);
    } 
}
void solve(int x){
  vis[x]=1;
  xu[++id]=0;p[id]={0,0};l=r=id;
  for (int i=point[x];i;i=next[i])
    if (!vis[e[i].en]){
      dis[e[i].en]=e[i].v;
      getroute(e[i].en,x);
      r=id;
    }
  for (int i=point[x];i;i=next[i])
    if (!vis[e[i].en]){
      root=0;
      sum=size[e[i].en];
      //cout<<e[i].en<<' '<<sum<<endl;
      getroot(e[i].en,0);
      //cout<<root<<endl;while (1);
      solve(root);
    }
}
int query(int l,int r){
  int t;
  t=(int)(log(r-l+1.0)/log(2.0));
  if (xu[f[t][l]]>xu[f[t][r-(1<<t)+1]])
    return f[t][l];
  else return f[t][r-(1<<t)+1];
}
void pre(){
  for (int i=1;i<=id;i++) f[0][i]=i;
  for (int i=1;i<=19;i++)
    for (int j=1;j<=id;j++)
      if (j+(1<<i)-1<=id){
        if (xu[f[i-1][j]]>xu[f[i-1][j+(1<<i>>1)]])
          f[i][j]=f[i-1][j];
        else f[i][j]=f[i-1][j+(1<<i>>1)];
      }
      else break;
  for (int i=1;i<=id;i++)
    if (p[i].l!=0)
      q.push(use{p[i].l,p[i].r,i,query(p[i].l,p[i].r)});
}
void getans(){
  for (int i=1;i<=m;i++){
    use t=q.top();q.pop();
    printf("%d\n",xu[t.mq]+xu[t.h]);
    if (t.l<=t.mq-1) q.push(use{t.l,t.mq-1,t.h,query(t.l,t.mq-1)});
    if (t.mq+1<=t.r) q.push(use{t.mq+1,t.r,t.h,query(t.mq+1,t.r)});
  }
}
int main(){
  //freopen("a.in","r",stdin);
  //freopen("a.out","w",stdout);
  n=read();m=read();
  for (int i=1;i<n;i++){
    x=read();y=read();v=read();
    add(x,y,v);add(y,x,v);
  } 
  sum=n;h[root]=n;
  getroot(1,0);
  //for (int i=1;i<=n;i++) cout<<h[i]<<endl;
  solve(root);
  pre();
  getans();
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值