NOIP2016提高组Day1T2 天天爱跑步 树链剖分 LCA 倍增 差分

原文链接https://www.cnblogs.com/zhouzhendong/p/9275606.html

题目传送门 - 洛谷P1600

题目传送门 - LOJ#2359

题目传送门 - Vijos P2004

题意

  给定一个有 $n$ 个节点的树,每一个节点有一个观察员,编号为 $i$ 的节点上的观察员会在 $W_i$ 时刻出来观察。

  现在有 $m$ 个热爱健身的人,其中第 $i$ 个从节点 $S_i$ 开始,到 $T_i$ 结束。

  从时刻 $0$ 开始,每一个人同时以每秒一条边的速度沿着他的起点终点的最短路径移动,移到 $T_i$ 时停止移动,然后下一秒“消失”。

  对于每一个节点,问在哪个位置的观察员会观察到几个人。

  数据范围:

  $1\leq n<300000,0\leq W_i\leq n,1\leq S_i,T_i\leq n$

题解

  看到原题中有一档链上询问的部分分,于是我们先考虑链上的情况。

  对于所有 $S_i\leq T_i$ ,我们发现第 $i$ 个人到达第 $j$ 个节点时, $T-j$ 为定值。(其中 $T$ 为当前的时间)

  即,若存在 $j$ 节点上面的观察员看到了这个人,那么必然有:

$$0-S_i=W_j-j=(T_i-S_i)-T_i$$

  那么我们只需要差分一下,用一个桶维护,在 $S_i$ 时给桶的 $0-S_i$ 位置加上 $1$ ,然后在 $T_i+1$ 位置减掉就可以了。

  于是我们只需要对于每一个 $i$ ,预处理一下要在桶上面进行的操作,给每一个点开一个 vector ,把需要的操作扔进去即可。

  然后顺着一遍扫过去处理,对第 $i$ 个节点的贡献就是 $Tax[W_i-i]$ 。

  

  对于所有 $S_i>T_i$ ,类似的,我们发现第 $i$ 个人到达第 $j$ 个节点时,$T+j$ 为定值。

  和上面类似的,我们可以完成这一部分的贡献。

 

  于是我们就可以解决链上的本问题了。

  时间复杂度 $\Theta(n)$ 。

 

  而原问题是在树上。

  那就树链剖分一下就可以了。

  复杂度多一只 $log$ ,因为每一条路径被分成了 $O(\log n)$ 条,对应的有 $O(n\log n)$ 个操作,每一个操作 $O(1)$。

  为了方便,我们用倍增求 $LCA$ 。当然写的时候一定要有清醒的头脑,千万注意细节。

  时空复杂度 $\Theta(n\log n)$ 。

代码

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <bits/stdc++.h>
#define dec D
using namespace std;
const int N=600005;
struct Gragh{
	int cnt,y[N],nxt[N],fst[N];
	void add(int a,int b){
		y[++cnt]=b,nxt[cnt]=fst[a],fst[a]=cnt;
	}
}g;
int n,m;
int fa[N][20],depth[N],size[N],son[N],top[N],p[N],ap[N],cnp=0;
int wtime[N],ans[N],itax[N],dtax[N];
vector <int> inc[N],dec[N];
void Get_Gen_Info(int x,int pre,int d){
	depth[x]=d,fa[x][0]=pre,son[x]=-1,size[x]=1;
	for (int i=1;i<20;i++)
		fa[x][i]=fa[fa[x][i-1]][i-1];
	for (int i=g.fst[x];i;i=g.nxt[i])
		if (g.y[i]!=pre){
			int y=g.y[i];
			Get_Gen_Info(y,x,d+1);
			size[x]+=size[y];
			if (son[x]==-1||size[y]>size[son[x]])
				son[x]=y;
		}
}
void Get_Top(int x,int Top){
	top[x]=Top;
	ap[p[x]=++cnp]=x;
	if (son[x]==-1)
		return;
	Get_Top(son[x],Top);
	for (int i=g.fst[x];i;i=g.nxt[i]){
		int y=g.y[i];
		if (y!=fa[x][0]&&y!=son[x])
			Get_Top(y,y);
	}
}
int LCA(int x,int y){
	if (depth[x]<depth[y])
		swap(x,y);
	for (int i=19;i>=0;i--)
		if (depth[x]-(1<<i)>=depth[y])
			x=fa[x][i];
	if (x==y)
		return x;
	for (int i=19;i>=0;i--)
		if (fa[x][i]!=fa[y][i])
			x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}
void Tupdate(int S,int T){
	int TS=0,TT=depth[S]+depth[T]-2*depth[LCA(S,T)];
	int FS=top[S],FT=top[T];
	while (FS!=FT)
		if (depth[FS]>depth[FT]){
			dec[p[FS]].push_back(TS+p[S]);
			dec[p[S]+1].push_back(-(TS+p[S]));
			TS+=depth[S]-depth[FS]+1;
			S=fa[FS][0],FS=top[S];
		}
		else {
			inc[p[FT]].push_back(TT-p[T]+n+1);
			inc[p[T]+1].push_back(-(TT-p[T]+n+1));
			TT-=depth[T]-depth[FT]+1;
			T=fa[FT][0],FT=top[T];
		}
	if (depth[S]>depth[T]){
		dec[p[T]].push_back(TT+p[T]);
		dec[p[S]+1].push_back(-(TS+p[S]));
	}
	else {
		inc[p[S]].push_back(TS+n-p[S]+1);
		inc[p[T]+1].push_back(-(TT+n-p[T]+1));
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for (int i=1,a,b;i<n;i++)
		scanf("%d%d",&a,&b),g.add(a,b),g.add(b,a);
	for (int i=1;i<=n;i++)
		scanf("%d",&wtime[i]);
	Get_Gen_Info(1,0,0);
	Get_Top(1,1);
	for (int i=1,S,T;i<=m;i++)
		scanf("%d%d",&S,&T),Tupdate(S,T);
	for (int i=1;i<=n;i++){
		for (int j=0;j<inc[i].size();j++){
			int v=inc[i][j],d=1;
			if (v<0)
				v=-v,d=-d;
			itax[v]+=d;
		}
		for (int j=0;j<dec[i].size();j++){
			int v=dec[i][j],d=1;
			if (v<0)
				v=-v,d=-d;
			dtax[v]+=d;
		}
		int x=ap[i];
		ans[x]=itax[wtime[x]+(n-i+1)]+dtax[wtime[x]+i];
	}
	for (int i=1;i<=n;i++)
		printf("%d ",ans[i]);
	return 0;
}

  

posted @ 2018-07-06 21:21  zzd233  阅读(287)  评论(0编辑  收藏  举报