树上的路径

树上的路径

时间限制: 1 Sec 内存限制: 256 MB
题目描述
给定一棵N个结点的树,结点用正整数1..N编号,每条边有一个正整数权值。用d(a,b)表示从结点a到结点b路径上经过边的权值和,其中要求a

题解

类似于noi2009超级钢琴的做法,先用点分治找出每个点能和它联通的点的区间,然后把这些东西扔到一个堆里,用主席树维护区间第K大即可。

代码

#include<iostream> 
#include<cstdio> 
#include<cstdlib> 
#include<cstring> 
#include<string> 
#include<cmath> 
#include<queue>
#include<algorithm> 
#define N 50010
#define inf 1050000000
using namespace std;
int n,m,cnt,size[N],w[N],fa[N],dis[N],s[N*17],c[N*17];
int k,la[N],ff[N*2],q[N],flag[N],st,maxn,tot;
struct node{int a,b,c;}map[N*2];
struct wbs{
  int l,r,pos,num1,num2;
  bool operator<(const wbs &cyc)
  const{return num1+num2<cyc.num1+cyc.num2;}
};
struct point{int lc,rc,size;}t[N*270];
priority_queue<wbs>h;

void add(int a,int b,int c)
{
  map[++k]=(node){a,b,c};ff[k]=la[a];la[a]=k;
  map[++k]=(node){b,a,c};ff[k]=la[b];la[b]=k;
}

int find(int S)
{
  int l=1,r=2,num=inf,res;q[1]=S;flag[S]=1;fa[S]=0;
  while(l<r)
  {
    int x=q[l];l++;size[x]=1;w[x]=0;
    for(int a=la[x];a;a=ff[a])
      if(!flag[map[a].b])
        q[r]=map[a].b,flag[q[r]]=1,fa[q[r]]=x,r++;  
  }
  if(r==2)return -1;
  for(int i=r-1;i;i--)
  {
    int x=q[i];flag[x]=0;
    if(fa[x])size[fa[x]]+=size[x],w[fa[x]]=max(w[fa[x]],size[x]);
    if(max(w[x],r-size[x]-1)<num)num=max(w[x],r-size[x]-1),res=x;
  }
  return res;
}

void work(int S,int top,int num)
{
  int l=1,r=2,end=cnt,tmp=0;
  q[1]=S;dis[S]=num;flag[S]=1;
  while(l<r)
  { 
    int x=q[l];l++;s[++cnt]=dis[x];tmp=max(tmp,dis[x]);
    for(int a=la[x];a;a=ff[a])
      if(!flag[map[a].b])
        q[r]=map[a].b,flag[q[r]]=1,dis[q[r]]=dis[x]+map[a].c,r++;
  }
  for(int i=r-1;i;i--)
  {
    int x=q[i];flag[x]=0;
    h.push((wbs){st,end,end-st+1,dis[x],maxn});
  }
  maxn=max(maxn,tmp);
}

void solve(int x)
{
  int rt=find(x);
  if(rt==-1)return;
  flag[rt]=1;st=++cnt;maxn=0;s[cnt]=0;
  for(int a=la[rt];a;a=ff[a])
    if(!flag[map[a].b])work(map[a].b,rt,map[a].c);
  for(int a=la[rt];a;a=ff[a])
    if(!flag[map[a].b])solve(map[a].b);
}

class seg_tree
{
  public:
  void modify(int x,int pre,int l,int r,int des)
  {
    t[x]=t[pre];t[x].size++;
    if(l==r)return;
    int mid=l+r>>1;
    if(des<=mid)modify(t[x].lc=++tot,t[pre].lc,l,mid,des);
    else modify(t[x].rc=++tot,t[pre].rc,mid+1,r,des);
  }
  int qry(int x,int pre,int l,int r,int des)
  {
    if(l==r)return c[l];
    int mid=l+r>>1,num=t[t[x].lc].size-t[t[pre].lc].size;
    if(des<=num)qry(t[x].lc,t[pre].lc,l,mid,des);
    else qry(t[x].rc,t[pre].rc,mid+1,r,des-num);
  }
}T;

int main()
{
  int a,b,v;
  scanf("%d%d",&n,&m);
  for(int i=1;i<n;i++)
    scanf("%d%d%d",&a,&b,&v),add(a,b,v);
  solve(1);
  for(int i=1;i<=cnt;i++)c[i]=s[i];
  sort(c+1,c+cnt+1);tot=cnt;
  int Tnum=unique(c+1,c+cnt+1)-c-1;
  for(int i=1;i<=cnt;i++)
    T.modify(i,i-1,1,Tnum,lower_bound(c+1,c+Tnum+1,s[i])-c);
  while(m--&&h.size())
  {
    wbs x=h.top();h.pop();
    printf("%d\n",x.num1+x.num2);
    if(x.pos==1)continue;
    x.num2=T.qry(x.r,x.l-1,1,Tnum,--x.pos);
    h.push(x);
  }
  return 0;
}

你可能感兴趣的:(点分治)