bzoj 3784: 树上的路径 点分治+dfs序+ST表

1 篇文章 0 订阅
1 篇文章 0 订阅

题意

给定一个N个结点的树,结点用正整数1..N编号。每条边有一个正整数权值。用d(a,b)表示从结点a到结点b路边上经过边的权值。其中要求a

分析

首先两点距离,先想到点分治
对于每个点,再对于每个分治重心,肯定可以找到分治中心中除这个点外的其它子树的最大值

然后我们建出点分树之后,跑出dfs序,然后找其它子树的最大值就是一段连续的区间

我们可以用st表维护区间最大的一个点的值,之后再把一个区间变成两个小区间,就像NOI2010的超级钢琴一样,然后就用 O(Nlog2N) O ( N l o g 2 N ) 解决了这个问题

卡卡常就可以了

代码

#include <bits/stdc++.h>
#define bin(i) (1<<(i))
using namespace std;
const int N = 50010;
char O[1<<14],*S=O,*T=O;
#define gc (S==T&&(T=(S=O)+fread(O,1,1<<14,stdin),S==T)?-1:*S++)
inline int read(){
  int x=0,f=1; char ch=gc;
  while(ch<'0' || ch>'9'){if(ch=='-')f=-1; ch=gc;}
  while(ch>='0' && ch<='9'){x=(x<<1)+(x<<3)+(ch^48); ch=gc;}
  return x*f;
}

inline void write(int x)
{
  int num=0; char ch[15];
  while(x) ch[++num]=x%10+'0',x/=10;
  while(num) putchar(ch[num--]);
  putchar('\n');
}
inline void _max(int &x,int y){x=x>y ? x : y;}
struct node{int x,y,next,d;}edge[N<<1],e[N]; int len,first[N],l;
void ins(int x,int y,int d){len++; edge[len].x=x; edge[len].y=y; edge[len].d=d; edge[len].next=first[x]; first[x]=len;}

bool bo[N]; int mxx = 0,p;
int siz[N];

inline void dfs2(int x,int f)
{
  siz[x] = 1;
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y = edge[k].y; if(y==f || bo[y]) continue;
    dfs2(y,x); siz[x] += siz[y];
  }
}

inline void find_root(int x,int f,int tot)
{
  int maxx = 0;
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y = edge[k].y;
    if(y==f || bo[y]) continue;
    _max(maxx,siz[y]); find_root(y,x,tot);
  }
  _max(maxx , tot - siz[x]);
  if(maxx < mxx)
  {
    mxx = maxx , p = x;
  }
}

int D[N][16];

inline void dfs3(int x,int f,int deep)
{
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y = edge[k].y;
    if(bo[y] || y==f) continue;
    D[y][deep] = D[x][deep] + edge[k].d; dfs3(y,x,deep);
  }
}

inline int dfs1(int x,int f,int deep)
{
  dfs2(x,0); mxx = INT_MAX; p=0; find_root(x,0,siz[x]); x=p;

  D[x][deep] = 0; dfs3(x,0,deep);

  bo[x] = 1; for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y = edge[k].y;
    if(y==f || bo[y]) continue;
    l++; e[l].x=x; e[l].y=dfs1(y,x,deep+1);
  }return x;
}

int s[N],d[N],h[N];  int id = 0; int fa[N],dep[N] , mx[16][N][16];
inline void dfs4(int x,int f)
{
  s[x] = ++id; h[id] = x; fa[x] = f;
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y = edge[k].y;
    dep[y] = dep[x] + 1; dfs4(y,x);
  }d[x] = id;
}

struct node1
{
  int x,y,d,l,r,ret; // dfn parent dep right
  node1(){}
  node1(int _x,int _y,int _d,int _l,int _r,int _ret){x=_x; y=_y; d=_d; l=_l; r=_r; ret = _ret;}
};

bool operator <(const node1 &x,const node1 &y) {return x.ret < y.ret;}

priority_queue<node1>q;
int lg2[N];

inline int find(int L,int R,int i)
{
  int dd = lg2[R-L+1];
  int x = h[mx[i][L][dd]]; int y = h[mx[i][R-bin(dd)+1][dd]];
  return (D[x][i] >= D[y][i]) ? s[x] : s[y];
}

inline int calc(node1 x){return D[h[x.x]][x.d] + D[h[x.y]][x.d];}

int main()
{
  int n = read(); int k = read(); memset(first,-1,sizeof(first)); len = 0;
  for(int i=1;i<n;i++)
  {
    int a = read(); int b = read(); int c = read();
    ins(a,b,c); ins(b,a,c);
  }

  for(int i=2;i<=n;i++) lg2[i] = lg2[i>>1] + 1;

  memset(bo,0,sizeof(bo));
  l=0; int rt = dfs1(1,0,0);

  len = 0; memset(first,-1,sizeof(first));
  for(int i=1;i<=l;i++) ins(e[i].x,e[i].y,0);
  dfs4(rt,0);

  for(int i=0;i<=15;i++)
  {
    for(int j=1;j<=n;j++) mx[i][s[j]][0] = s[j];
    for(int j=1;j<=15;j++) for(int kk=1;kk<=n;kk++) if(kk+(1<<j)-1<=n)
    {
      int x,y; x = h[mx[i][kk][j-1]]; y = h[mx[i][kk+(1<<(j-1))][j-1]];
      mx[i][kk][j] = (D[x][i] >= D[y][i]) ? s[x] : s[y];
    } 
  }

  for(int i=1;i<=n;i++)
  {
    int x = fa[i]; int lst = i;
    while(x)
    {
      int L = d[lst]+1; int R = d[x];
      if(L<=R)
      {
        node1 xx = node1(s[i],find(L,R,dep[x]),dep[x],L,R,0);
        int c = calc(xx); xx.ret=c; q.push(xx);
      }
      lst = x; x = fa[x];
    }
    if(s[i]<d[i])
    {
      node1 xx = node1(s[i],find(s[i]+1,d[i],dep[i]),dep[i],s[i]+1,d[i],0);
      int c = calc(xx); xx.ret=c;
      q.push(xx);
    }
  }

  while(k--)
  {
    node1 x = q.top(); q.pop();
    write(calc(x));
    int L = x.l; int R = x.y-1; node1 xx = node1(x.x,find(L,R,x.d),x.d,L,R,0);
    int c = calc(xx); xx.ret=c;
    if(L<=R) q.push(xx);

    L = x.y+1; R = x.r; xx = node1(x.x,find(L,R,x.d),x.d,L,R,0);
    c = calc(xx); xx.ret=c;
    if(L<=R) q.push(xx);
  }

  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值