[BZOJ3011][Usaco2012 Dec]Running Away From the Barn

题意

给出一棵以1为根节点树,求每个节点的子树中到该节点距离<=l的节点的个数

题解

方法1:倍增+差分数组

首先可以很容易的转化问题,考虑每个节点对哪些节点有贡献

即每次对于一个节点,找到其第l个父亲,这个操作可以用倍增在logn时间内完成

找到后将x-y这一段区间都加1,很容易想到用差分数组维护

方法2:主席树

考虑节点x和节点x的子树中的一个节点y,记点x到根节点的距离为dis[x]

若dis[y]-dis[x]<=l则满足条件

将不等式变形可得dis[y]<=dis[x]+l

即对每个点以dis[ ]为权值插入树中,查询时查找比dis[x]+l小的节点个数

这时很容易想到主席树,第一维对dis[x](维护的前缀也就是查询的答案),第二维对节点编号

又由于先决条件是节点y是在x的子树中的,所以我们应弄出一个dfs序

对于每一个节点,其子树即num[x]-----子树中num[y]的最大值

其次,插入时为避免对之后的节点造成影响,应将dis[]从小到大进行插入

注意到dis数据范围较大考虑对其离散化

还有就是,主席树的题目空间往往不能浪费太多

方法3:左偏树

可以考虑,对于节点x的子树中的一个节点

若x与这个节点的距离>l,那么x的父亲与这个节点的距离也一定>l

所以可以考虑用左偏树来维护

对于每个节点x,枚举它的儿子y,维护一个大根堆

另外,两个点之间的距离可以用dis[1,y]-dis[1,x]表示

**没仔细看题数据是longlong啊。。

代码

方法1:倍增+差分数组

方法2:主席树

uses math;
type re=record
  a,b,c,num:int64;
end;
type ree=record
  h,t,x:longint;
end;
var
  i,j:longint;
  now,m,n,c,d,k,l,o,ans,x,tmp:int64;
  a,dis:array[1..500000]of re;
  f:array[1..500000]of boolean;
  num,fa,head,q:array[0..500000]of int64;
  p:array[0..5000000]of ree;
procedure arr(x,y,z:int64);
begin
  inc(l);
  a[l].a:=head[x];
  a[l].b:=y;
  a[l].c:=z;
  head[x]:=l;
end;
function dfs(x,y:int64):int64;
var u,v:longint;
begin
  f[x]:=false; dis[x].a:=y; dis[x].b:=x;
  inc(now); dis[x].num:=now;
  u:=head[x]; dfs:=now;
  while u<>0 do
  begin
    v:=a[u].b;
    if f[v] then dfs:=max(dfs,dfs(v,y+a[u].c));
    u:=a[u].a;
  end;
  dis[x].c:=dfs;
end;
procedure swap(var x,y:re);
var tmp:re;
begin
  tmp:=x; x:=y; y:=tmp;
end;
procedure qsort(h,t:int64);
var i,j,mid:int64;
begin
  i:=h; j:=t; mid:=dis[(h+t) div 2].a;
  repeat
    while dis[i].a<mid do inc(i);
    while dis[j].a>mid do dec(j);
    if i<=j then
    begin
      swap(dis[i],dis[j]);
      inc(i); dec(j);
    end;
  until i>j;
  if i<t then qsort(i,t);
  if h<j then qsort(h,j);
end;
procedure build(x,h,t:int64);
var mid:int64;
begin
  p[x].h:=x*2; p[x].t:=x*2+1; now:=max(now,x*2+1);
  if h=t then exit;
  mid:=(h+t) div 2;
  build(x*2,h,mid); build(x*2+1,mid+1,t);
end;
function find(x:int64):int64;
var h,t,mid:int64;
begin
  h:=1; t:=n;
  while h<t do
  begin
    mid:=(h+t) div 2+1;
    if dis[mid].a<=x then h:=mid else t:=mid-1;
  end;
  exit(h);
end;
procedure insert(pre,x,h,t:int64);
var tmp,mid:longint;
begin
  inc(now); p[now]:=p[pre]; inc(p[now].x); tmp:=now;
  if h=t then exit;
  mid:=(h+t) div 2;
  if (x<=mid) then
  begin
    insert(p[now].h,x,h,mid);
    p[tmp].h:=tmp+1;
  end else
  begin
    insert(p[now].t,x,mid+1,t);
    p[tmp].t:=tmp+1;
  end;
end;
function query(x,h1,t1,h,t:int64):int64;
var mid:int64;
begin
  if (h>t1) or (t<h1) then exit(0);
  if (h<=h1) and (t1<=t) then exit(p[x].x);
  mid:=(h1+t1) div 2;
  exit(query(p[x].h,h1,mid,h,t)+query(p[x].t,mid+1,t1,h,t));
end;
begin
  readln(n,k);  fillchar(f,sizeof(f),true);
  for i:=1 to n-1 do
  begin
    read(c,d); arr(i+1,c,d); arr(c,i+1,d);
  end;
  dfs(1,0);
  qsort(1,n); o:=0;
  for i:=1 to n do
  begin
    if (i=1) or (dis[i].a<>dis[i-1].a) then
      inc(o);
    num[i]:=o;
  end;
  now:=0; build(1,1,n);
  fa[0]:=1;
  for i:=1 to n do
  begin
    if (i<>1) and (num[i]=num[i-1]) then
    begin
      tmp:=now;
      insert(fa[num[i]],dis[i].num,1,n);
      fa[num[i]]:=tmp+1;
    end
    else
    begin
      fa[num[i]]:=now+1;
      insert(fa[num[i]-1],dis[i].num,1,n);
    end;
  end;
  for i:=1 to n do
  begin
    x:=find(dis[i].a+k);
    ans:=query(fa[num[x]],1,n,dis[i].num,dis[i].c);
    q[dis[i].b]:=ans;
  end;
  for i:=1 to n do writeln(q[i]);
end.

 方法3:左偏树

type re=record
  a,b,c:int64;
end;
var
  i,j:longint;
  m,n,ans,l,c,d:int64;
  k:int64;
  f:array[1..1000000]of boolean;
  a:array[1..1000000]of re;
  dis,left1,right1,fa,cnt,ll,head,vv,num:array[0..1000000]of int64;
procedure arr(x,y,z:int64);
begin
  inc(l);
  a[l].a:=head[x];
  a[l].b:=y;
  a[l].c:=z;
  head[x]:=l;
end;
function getfa(x:int64):int64;
begin
  while (fa[x]<>0) do x:=fa[x];
  exit(x);
end;
procedure swap(var x,y:int64);
var tmp:int64;
begin
  tmp:=x; x:=y; y:=tmp;
end;
function merge(x,y:int64):int64;
var tmp:int64;
begin
  if (x=0) or (y=0) then exit(x+y);
  if (dis[x]<dis[y]) then swap(x,y);
  right1[x]:=merge(right1[x],y);
  fa[right1[x]]:=x;
  if (ll[left1[x]]<ll[right1[x]]) then swap(left1[x],right1[x]);
  ll[x]:=ll[right1[x]]+1; cnt[x]:=cnt[left1[x]]+cnt[right1[x]]+1;
  exit(x);
end;
function delete(x:int64):int64;
var tmp:int64;
begin
  fa[left1[x]]:=0; fa[right1[x]]:=0;
  if left1[x]<>0 then tmp:=left1[x] else tmp:=right1[x];
  merge(left1[x],right1[x]);
  exit(getfa(tmp));
end;
procedure dfs(x,y:int64);
var u,v,c,d,goal,ans,z:int64;
begin
  ans:=0;  f[x]:=false; dis[x]:=y;
  u:=head[x];
  while u<>0 do
  begin
    v:=a[u].b;
    if f[v] then
        begin
          dfs(v,y+a[u].c);
          c:=getfa(v); goal:=y+k;
          while (c<>0)  and (dis[c]>goal) do
          begin
            c:=delete(c);
          end;
          if c<>0 then
          begin
            ans:=ans+cnt[c];
            z:=getfa(x);
            merge(z,c);
          end;
        end;
    u:=a[u].a;
  end;
  if k>=0 then vv[x]:=ans+1 else vv[x]:=ans;
end;
begin
  readln(n,k);
  if k<0 then writeln('111');
  for i:=1 to n-1 do
  begin
    read(c,d); arr(i+1,c,d); arr(c,i+1,d);
  end;
  for i:=1 to n do cnt[i]:=1;
  fillchar(f,sizeof(f),true);
  dfs(1,0);
  for i:=1 to n do writeln(vv[i]);
end.
原文地址:https://www.cnblogs.com/yinwuxiao/p/7966221.html