简单数据结构 题解

题目链接

左偏树做法

由于 \(m - k\) 的特性,每个节点的 \(m - k\) 一定小于等于他的任何一个子节点的,因此可以把每个节点的子树中的每个节点存进一个大根可并堆,即左偏树,中,然后不断 \(pop\) 堆中最大的元素直到堆中元素有且仅有 \(m - k\) 个。至于快速入堆,就是将一个节点的所有子节点的堆合并在一起,同时把自己的元素放进去,由于 \(m - k\) 的特性,可以保证不会出现一个节点把子节点和堆合并在一起后元素不足 \(m - k\) 的情况。

Show Code
#include
#define int long long
using namespace std;
inline int mread(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if(ch == '-')
        f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    x = x * 10 + ch - '0', ch = getchar();
    return x * f;
}
const int N = 5e5 + 5;
int n, q, m, a[N], f[N], ans[N], sum[N], fa[N], s2[N], dist[N], lx[N], rx[N];
char s[N];
vector v[N];
int findfa(int x){
	if(fa[x] == x)
	return x;
	return fa[x] = findfa(fa[x]);
}
int zmerge(int x, int y){
	if(!x || !y)
	return x | y;
	if(a[x] < a[y])
	swap(x, y);
	rx[x] = zmerge(rx[x], y);
	if(dist[lx[x]] < dist[rx[x]])
	swap(lx[x], rx[x]);
	dist[x] = dist[lx[x]] + dist[rx[x]] + 1;
	return x;
}
int zpop(int x){
	return fa[x] = fa[lx[x]] = fa[rx[x]] = zmerge(lx[x], rx[x]);
}
void dfs(int x, int fa2){
	if(s[x] == '1')
	sum[x] = 1;
	for(int i = 0; i < v[x].size(); i ++){
		int y = v[x][i];
		if(y == fa2)
		continue;
		dfs(y, x);
		sum[x] += sum[y];
		s2[x] += s2[y];
		fa[findfa(x)] = fa[findfa(y)] = zmerge(findfa(x), findfa(y));
	}
	if(m <= sum[x] || m - sum[x] > s2[x]){
		ans[x] = -1;
		return;
	}
	while(s2[x] > m - sum[x]){
		zpop(findfa(x));
		s2[x] --;
	}
	ans[x] = a[findfa(x)];
	return;
}
signed main(){
	n = mread(), q = mread(), m = mread();
	for(int i = 1; i <= n; i ++){
		a[i] = mread();
		fa[i] = i;
		s2[i] = 1;
		dist[i] = 1;
	}
	for(int i = 2; i <= n; i ++){
		f[i] = mread();
		v[f[i]].push_back(i);
	}
	scanf("%s", s + 1);
	dfs(1, 0);
	for(int i = 1, x; i <= q; i ++){
		x = mread();
		printf("%lld\n", ans[x]);
	}
	return 0;
}

主席树做法

直接使用主席树查询第 \(k\) 小的模板,至于每个查询的查询区间是多少,可以先将整棵树 dfs 一遍,然后每个节点的子树中的所有节点的 dfs 序一定连续,可以使用主席树直接解决。

posted @ 2024-01-21 15:08  cndark_moon  阅读(22)  评论(0)    收藏  举报