CF932D Tree

题目链接

题目

见链接。

题解

知识点:倍增。

注意到,题目其实要求我们,每次要选最近一个权值大于等于自己的祖先,可以看出固定点生成出来的序列是固定的。因此,考虑设 fi,j 为从 j 出发按规则向上走 2i 次的点,设 bi,j 为从 j 出发按规则向上走 2i 次的所有序列点的权值和。

对于查询,需要注意要手动避免越界的 0 号节点问题,其余和正常倍增一致。

对于修改,由于是尾部追加,因此倍增可以支持,需要先确定向上第一个点是谁:

  1. 若新加点比它父亲小,那么直接设为它父亲。
  2. 否则,从父亲出发生成的序列中,找到比自己大的第一个点,显然可以用倍增找到。

随后更新一下倍增数组即可。

时间复杂度 O(QlogQ)

空间复杂度 O(QlogQ)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 400007;
int cnt;
int a[N];
int f[27][N];
ll b[27][N];
void update(int u, ll w) {
a[++cnt] = w;
if (a[u] >= a[cnt]) {
f[0][cnt] = u;
b[0][cnt] = a[u];
}
else {
for (int i = 20;i >= 0;i--) {
if (!f[i][u]) continue;
if (a[f[i][u]] < a[cnt]) u = f[i][u];
}
f[0][cnt] = f[0][u];
b[0][cnt] = a[f[0][u]];
}
for (int i = 1;i <= 20;i++) {
f[i][cnt] = f[i - 1][f[i - 1][cnt]];
b[i][cnt] = b[i - 1][cnt] + b[i - 1][f[i - 1][cnt]];
}
}
int get_ans(int u, ll k) {
if (a[u] > k) return 0;
int ans = 1;
ll sum = a[u];
for (int i = 20;i >= 0;i--) {
if (!f[i][u]) continue;
if (sum + b[i][u] <= k) {
sum += b[i][u];
ans += 1 << i;
u = f[i][u];
}
}
return ans;
}
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
++cnt;
int Q;
cin >> Q;
ll last = 0;
while (Q--) {
int op;
ll p, q;
cin >> op >> p >> q;
if (op == 1) {
int r = p ^ last;
ll w = q ^ last;
update(r, w);
}
else {
int r = p ^ last;
ll x = q ^ last;
cout << (last = get_ans(r, x)) << '\n';
}
}
return 0;
}
posted @   空白菌  阅读(14)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 全程使用 AI 从 0 到 1 写了个小工具
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)
历史上的今天:
2022-06-23 NC50038 kotori和糖果
2022-06-23 Contest
2022-06-23 NC19115 选择颜色
2022-06-23 NC23046 华华教月月做数学
2022-06-23 NC14731 逆序对
2022-06-23 NC15052 求最值
2022-06-23 NC50999 表达式计算4
点击右上角即可分享
微信分享提示