JLOI2014 | 松鼠的新家(树上差分)

题目描述

松鼠的新家是一棵树,前几天刚刚装修了新家,新家有 (n) 个房间,并且有 (n-1) 根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的。天哪,他居然真的住在“树”上。

松鼠想邀请小熊前来参观,并且还指定一份参观指南,他希望小熊能够按照他的指南顺序,先去 (a_1) ,再去 (a_2)(cdots),最后到 (a_n),去参观新家。可是这样会导致重复走很多房间,懒惰的维尼不停地推辞。可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃。

小熊是个馋家伙,立马就答应了。现在松鼠希望知道为了保证维尼有糖果吃,他需要在每一个房间各放至少多少个糖果。

因为松鼠参观指南上的最后一个房间 (a_n) 是餐厅,餐厅里他准备了丰盛的大餐,所以当维尼在参观的最后到达餐厅时就不需要再拿糖果吃了。

输入格式

第一行一个正整数 (n),表示房间个数第二行 (n) 个正整数,依次描述 (a_1, a_2,cdots,a_n)

接下来 (n-1) 行,每行两个正整数 (x,y),表示标号 (x)(y) 的两个房间之间有树枝相连。

输出格式
一共 (n) 行,第 (i) 行输出标号为 (i) 的房间至少需要放多少个糖果,才能让小熊有糖果吃。

输入输出样例

输入 #1

5
1 4 5 3 2
1 2
2 4
2 3
4 5

输出 #1

1
2
1
2
1

说明/提示

对于全部的数据,(2 le n le 3 imes 10^52≤n≤3×10)

————————————————————————————————

首先考虑一下题意,输入的第二行给定了松鼠的访问节点的顺序,由于树上两点的路径唯一,所以全程路径已知。

对于这种关心树上路径的问题,树链剖分可做,但这东西我很久很久没写过了,所以用了一个思路比较简单的树上差分(基于点)的方法。

Link: 浅谈差分和树上差分算法的基本应用

LCA 是用倍增实现的,常数有点大,最慢的一个点在 Luogu 上跑了 927ms

具体细节请见代码的注释

代码如下

#include <bits/stdc++.h>
#define MAXN 300007
using namespace std;
int n,cf[MAXN],dep[MAXN];
int a[MAXN],ans[MAXN],fa[MAXN][20];
vector<int> G[MAXN]; 
inline int readint() { //快速读入
	int w=0,X=0; char ch=0;
	while (!isdigit(ch)) w|=ch=='-',ch=getchar();
	while (isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
	return w?-X:X;
}
void dfs(int u,int f) {
	fa[u][0]=f,dep[u]=dep[f]+1;
	for (int i=0;i<(int)G[u].size();i++)
		if (G[u][i]!=f) dfs(G[u][i],u);
}
int lca(int a,int b) {
	if (dep[a]>dep[b]) swap(a,b); //先让 b 在下面 
        //首先跳到同一深度
	int c=dep[b]-dep[a];
	for (int j=0;(1<<j)<=c;j++)
		if ((1<<j)&c) b=fa[b][j];
	//一起往上跳
        if (a!=b) {
		for (int j=19;j>=0;j--)
			if (fa[a][j]!=fa[b][j]) a=fa[a][j],b=fa[b][j];
		a=fa[a][0];
	}
	return a;
}
void stat(int u) { //用差分数组统计答案
	ans[u]=cf[u];
	for (int i=0;i<(int)G[u].size();i++) {
		if(G[u][i]==fa[u][0]) continue;
		stat(G[u][i]); 
		ans[u]+=ans[G[u][i]];
	}
}
int main() {
	memset(cf,0,sizeof(cf)); //差分数组
	memset(fa,0,sizeof(fa)); //fa[i][j] 表示从 i 往上跳 2^j 次到达的节点
	memset(dep,0,sizeof(dep)); //节点的深度
	memset(ans,0,sizeof(ans)); //答案数组
	n=readint();
	for (int i=1;i<=n;i++) a[i]=readint(); 
	for (int i=1;i<n;i++) {
		int x=readint(),y=readint();
		G[x].push_back(y),G[y].push_back(x);
	}
        //倍增求 LCA 的预处理
	dfs(1,0);
	for (int j=1;j<20;j++)
		for (int i=1;i<=n;i++)
			fa[i][j]=fa[fa[i][j-1]][j-1];
	for (int i=1;i<n;i++) {
		cf[a[i]]++,cf[a[i+1]]++;
		cf[lca(a[i],a[i+1])]--,cf[fa[lca(a[i],a[i+1])][0]]--; //查封操作		
	}
	stat(1);
	for (int i=1;i<=n;i++) printf("%d
",a[1]==i?ans[i]:ans[i]-1); 
	return 0;
}
原文地址:https://www.cnblogs.com/zhwer/p/13167611.html