【自己出题】水滴(ST表,树)

题目

描述

在学校的路旁,有两排树,一排是杏树,另一排也是杏树。
每棵树可以看作是由\(n\)个点和\(n-1\)条枝条构成的。一个雨滴落在了一棵树上的点\(u\),他在重力的牵引下向下流动。他每秒都会流过一条边,直到抵达根节点,融进大地。
他漫无目的地向下移动,在\(t\)秒后,他突然和另一滴雨撞在了一起。刹那间,他的电子飞速运动,思考着与他相碰的这个雨滴最初可能是落在了哪些点上。

输入

第一行有一个整数\(n(1\le n \le 10^5)\),表示树的节点数。树的根节点是\(1\)
第二行有\(n-1\)个整数,其中第\(i\)个整数\(a_i(1\le a_i \le i)\)表示一条树枝的两个端点是\(i+1\)\(a_i\)
第三行有一个整数\(T(1\le T \le 10^5)\),表示数据组数。
接下来\(T\)行,每行有两个整数\(u,t(1\le u \le n; 0\le t \le n)\),保证数据合理。

输出

\(T\)行,每行一个整数,表示答案。

sample

input

5
1 2 2 4
1
3 1

output

3

hint

对于样例一,

另一滴水可能是从2,4,5这三个点掉落的。

题解

设一个以a为根节点的子树,它包含结点的个数为size[a]。如果t=0,那么答案为size[u];否则,设u的第t-1个父亲是v,第t个父亲是fa[v],那么答案是size[fa[v]]-size[v]。
size数组和fa数组进行一遍dfs,很容易求出来。重点在于如何快速求出u的第x个父亲。
这个问题可以用ST表解决。即令f[u][i]表示u的第\(2^i\)个父亲是哪个点,i的范围是0~20,f数组可以在dfs的过程中求出来,方程是:

\[f[u][0]=fa[u]\\ f[u][i]=f[f[u][i-1]][i-1],(i>0) \]

接下来就可以在O(log(t))的时间复杂度内求出u的第t个父亲。例如,t=11,它写成二进制是1011B,其中第0、1、3位是1,那么要求的结点就是 f[ f[ f[u][0] ][1] ][3]。

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

const int N=1e5+10;
vector<int> V[N];

int n;
int sz[N], f[N][20], dep[N];

void Add(int u, int v){
	V[u].push_back(v);
	V[v].push_back(u);
}

void dfs(int u, int fa){
	sz[u]=1;
	for(int i=1; i<20; ++i) f[u][i]=f[f[u][i-1]][i-1];
	for(auto&v:V[u]){
		if(v==fa) continue;
		f[v][0]=u;
		dfs(v,u);
		sz[u]+=sz[v];
	}
}

int getlca(int u, int len){
	for(int i=0; i<20; ++i) if(len&(1<<i)) u=f[u][i];
	return u;
}

int main(){
	scanf("%d", &n);
	for(int i=2, fa; i<=n; ++i){
		scanf("%d", &fa);
		Add(i,fa);
	}
	dfs(1,0);
	//t
	int T; cin>>T;
	while(T--){
		int u, t;
		scanf("%d%d", &u, &t);
		if(t==0) printf("%d\n", sz[u]);
		else{
			int v=getlca(u,t-1);
			printf("%d\n", sz[f[v][0]]-sz[v]);
		}
	}
}

数据生成

造数据有一个原则,那就是大小要小,否则上传数据就要耗费半天。因此我只造了6个数据,总大小为5M多,上传得还算快。
其中5个都是n=100000级别的数据,当然要随机生成了。
我通过控制每个节点与父节点标号的差的范围来控制树的形状,主要有下面三种情况:
对于每个结点i+1的父亲,一种情况是random(1,i),这样的树是完全随机生成的,我也不知道长什么样子;一种是random(1,min(i,50)),这样的树是非常扁平的;一种是random(max(1,i-100),i),这样的树是很高的。
我还构造了一个n=1的特别的测试点:

1

2
1 0
1 0

它的答案应该是:

1
1

数据生成代码:

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

const int N=1e5+10;
mt19937 rnd(time(0));
vector<int> V[N];

int rand(int l, int r){
	if(l==r) return l;
	return rnd()%(r-l)+l;
}

int n, T;
int sz[N], dep[N];
FILE *fw;

void dfs(int u, int fa){
//	printf("u=%d fa=%d\n", u, fa);
	sz[u]=1; dep[u]=dep[fa]+1;
	for(auto&v:V[u]) if(v!=fa){
		dfs(v,u);
		sz[u]+=sz[v];
	}
}

void fun(){
	n=100000;
	fprintf(fw, "%d\n", n);
	for(int i=1; i<=n; ++i){
		V[i].clear();
		sz[i]=0;
		dep[i]=0;
	}
	for(int i=1, u; i<n; ++i){
		fprintf(fw, "%d%c", u=rand(1,min(i,50)), " \n"[i==n-1]);
		V[u].push_back(i+1);
		V[i+1].push_back(u);
	}
	dfs(1,0);
	T=n;
	fprintf(fw, "%d\n", T);
	for(int i=1; i<=T; ++i){
		fprintf(fw, "%d %d\n", i, rand(0,dep[i]-1));
	}
}

int main(){
	char s[100];
	for(int i=5; i<=5; ++i){
		sprintf(s,"%d.in", i);
		fw=fopen(s,"w");
		fun();
		fclose(fw);
	}
}
posted @ 2021-12-10 00:11  white514  阅读(43)  评论(0编辑  收藏  举报