【NOIP2016】BZOJ4719天天爱跑步题解(树上差分+LCA+桶)

题目:BZOJ4719.
题目大意:给定一棵 n n n个点的树和树上每个节点的 w i w_i wi,现在给出 m m m s i s_i si t i t_i ti,表示从 s i s_i si t i t_i ti会有一个人沿树上的路径走过,并且这个人每秒移动到下一个点.现在每个人都在时刻 0 0 0走出,询问每一个点 i i i,请你输出第 w i w_i wi时刻有多少人会在点 i i i.
1 ≤ n , m ≤ 3 ∗ 1 0 5 1\leq n,m\leq 3*10^5 1n,m3105.

考虑先把每个人对应的有向路径拆成两部分,一部分是从起点到LCA,另一部分是从LCA到终点.

接下来我们只讨论第一部分的统计,第二部分类似.

首先我们把所有人时间定义为他的深度,这样子一个人会在第一部分被点 i i i统计当且仅当他会走过点 i i i且他的时间 = w i + d e p [ i ] =w_i+dep[i] =wi+dep[i].

那么我们就可以考虑树上差分.我们在树上每个点开个二元组vector,然后对于第 i i i个人,在他起点的vector里增加一个二元组表示增加这个人的贡献,在他LCA增加一个二元组表示减少这个人的贡献.

现在我们dfs一遍整棵树,考虑需要统计的东西.显然答案为一个点 k k k为根的子树中的所有点vector中的时间为 w [ k ] + d e p [ k ] w[k]+dep[k] w[k]+dep[k]的人的贡献和.

为了维护这个东西,我们考虑在dfs的过程中维护一个桶记录对应时间的贡献和,当进入一个点的时候先把这个点的答案减去当前桶中对应时间的贡献,再给桶中加入这个点vector的贡献,在退出前再加上当前桶中对应时间的贡献和就好了.

时间复杂度 O ( n + m log ⁡ n ) O(n+m\log n) O(n+mlogn).

注意处理LCA只能算一次的细节.

代码如下:

##include<bits/stdc++.h>
using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=300000;

int n,m;
struct side{
  int y,next;
}e[N*2+9];
int lin[N+9],cs;

void Ins(int x,int y){e[++cs].y=y;e[cs].next=lin[x];lin[x]=cs;}
void Ins2(int x,int y){Ins(x,y);Ins(y,x);}

int w[N+9];
int st[N+9],td[N+9];
struct node{
  int fa,son,dep,siz,top;
}d[N+9];

void Dfs_ord1(int k,int fa){
  d[k].fa=fa;
  d[k].dep=d[fa].dep+1;
  d[k].siz=1;
  for (int i=lin[k];i;i=e[i].next)
    if (e[i].y^fa){
    	Dfs_ord1(e[i].y,k);
    	d[k].siz+=d[e[i].y].siz;
    	if (d[e[i].y].siz>d[d[k].son].siz) d[k].son=e[i].y;
    }
}

void Dfs_ord2(int k,int top){
  d[k].top=top;
  if (d[k].son) Dfs_ord2(d[k].son,top);
  for (int i=lin[k];i;i=e[i].next)
    if (e[i].y^d[k].fa&&e[i].y^d[k].son) Dfs_ord2(e[i].y,e[i].y);
}

int Query_lca(int x,int y){
  for (;d[x].top^d[y].top;x=d[d[x].top].fa)
    if (d[d[x].top].dep<d[d[y].top].dep) swap(x,y);
  return d[x].dep<d[y].dep?x:y;
}

vector<int>a[2][N+9],v[2][N+9];

int Insert(int id,int k,int t,int x){a[id][k].push_back(t);v[id][k].push_back(x);}

int cnt[2][N*2+9],ans[N+9];

void Dfs_ans(int k){
  if (w[k]<=n) ans[k]-=cnt[0][w[k]+d[k].dep]+cnt[1][w[k]-d[k].dep+n];
  int siz=a[0][k].size();
  for (int i=0;i<siz;++i) cnt[0][a[0][k][i]]+=v[0][k][i];
  siz=a[1][k].size();
  for (int i=0;i<siz;++i) cnt[1][a[1][k][i]]+=v[1][k][i];
  for (int i=lin[k];i;i=e[i].next)
    if (e[i].y^d[k].fa) Dfs_ans(e[i].y);
  if (w[k]<=n) ans[k]+=cnt[0][w[k]+d[k].dep]+cnt[1][w[k]-d[k].dep+n];
}

void Get_ans(){
  for (int i=1;i<=m;++i){
  	int lca=Query_lca(st[i],td[i]),t0=d[st[i]].dep,t1=d[st[i]].dep-2*d[lca].dep+n;
  	Insert(0,st[i],t0,1);
  	Insert(0,d[lca].fa,t0,-1);
  	Insert(1,td[i],t1,1);
  	Insert(1,lca,t1,-1);
  }
  Dfs_ans(1);
}

Abigail into(){
  scanf("%d%d",&n,&m);
  for (int i=1;i<n;++i){
  	int x,y;
  	scanf("%d%d",&x,&y);
  	Ins2(x,y);
  }
  for (int i=1;i<=n;++i)
    scanf("%d",&w[i]);
  for (int i=1;i<=m;++i)
    scanf("%d%d",&st[i],&td[i]);
}

Abigail work(){
  Dfs_ord1(1,0);
  Dfs_ord2(1,1);
  Get_ans();
}

Abigail outo(){
  for (int i=1;i<=n;++i)
    printf("%d ",ans[i]);
  puts("");
}

int main(){
  into();
  work();
  outo();
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值