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

扫描二维码关注公众号,回复: 11320108 查看本文章

说明/提示

对于全部的数据,\(2 \le n \le 3 \times 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\n",a[1]==i?ans[i]:ans[i]-1); 
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/zhwer/p/13167611.html
今日推荐