从底向上考虑,如果当前节点 需要 块钱,它的兄弟(拥有相同父亲的其他节点)为 ,那么它的父亲需要 块钱才能保证 拿到 块钱,其中 是 的子树点权和。
对于每一个节点,暴力往上跳是很完蛋的,树剖或者倍增跳也很难,因为跳很多级父亲后 的增量根据 大小的不同也会不一样。
考虑一次跳跃,如果有 ,那么 至少翻倍,称这个点为倍增点。注意到一个点 跳到根的过程中至多有 个倍增点,而在跳到倍增点之前的路径,其对 的增量是很好预处理出然后快速求得的。
那么考虑一个倍增, 代表从 跳到 的 级父亲,要求 才能保证不会遇到倍增点,这个是很好预处理的,那么处理一个点的答案的时候倍增找倍增点,然后跳到倍增点上,在倍增点上 的增量统计可以提前给这个节点的儿子点权和排序然后二分来快速求得。
时间复杂度为 .
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#define pb emplace_back
#define mp std::make_pair
#define fi first
#define se second
typedef long long ll;
typedef std::pair<int, int> pii;
typedef std::pair<ll, int> pli;
typedef std::pair<ll, ll> pll;
typedef std::vector<int> vi;
typedef std::vector<ll> vll;
const ll mod = 998244353;
ll Add(ll x, ll y) { return (x+y>=mod) ? (x+y-mod) : (x+y); }
ll Mul(ll x, ll y) { return x * y % mod; }
ll Mod(ll x) { return x < 0 ? (x + mod) : (x >= mod ? (x-mod) : x); }
ll cadd(ll &x, ll y) { return x = (x+y>=mod) ? (x+y-mod) : (x+y); }
template <typename T> T Max(T x, T y) { return x > y ? x : y; }
template <typename T> T Min(T x, T y) { return x < y ? x : y; }
template <typename T>
T &read(T &r) {
r = 0; bool w = 0; char ch = getchar();
while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
while(ch >= '0' && ch <= '9') r = r * 10 + (ch ^ 48), ch = getchar();
return r = w ? -r : r;
}
const int N = 300010;
int n, fa[N][20];
ll su[N][20], val[N], f[N][20];
vi eg[N];
vll s[N], va[N];
void dfs1(int x) {
for(auto v : eg[x]) {
dfs1(v);
val[x] += val[v];
}
}
void dfs2(int x) {
for(int i = 1; i <= 19; ++i) fa[x][i] = fa[fa[x][i-1]][i-1];
if(x) {
if(va[fa[x][0]].size() == 1) f[x][0] = 0;
else f[x][0] = va[fa[x][0]][(int)va[fa[x][0]].size()-1-(va[fa[x][0]][(int)va[fa[x][0]].size()-1] == val[x])];
su[x][0] = s[fa[x][0]][(int)s[fa[x][0]].size()-1] - val[x];
for(int i = 1; i <= 19; ++i)
su[x][i] = su[x][i-1] + su[fa[x][i-1]][i-1],
f[x][i] = Max(f[x][i-1], f[fa[x][i-1]][i-1] - su[x][i-1]);
}
for(auto v : eg[x]) dfs2(v);
}
signed main() {
// freopen("data.in", "r", stdin);
// freopen("1.out", "w", stdout);
read(n);
for(int i = 1; i <= n; ++i) {
read(fa[i][0]); read(val[i]);
eg[fa[i][0]].pb(i);
}
dfs1(0);
for(int i = 1; i <= n; ++i) va[fa[i][0]].pb(val[i]), s[fa[i][0]].pb(0);
for(int i = 0; i <= n; ++i) {
std::sort(va[i].begin(), va[i].end());
for(int j = 0; j < (int)va[i].size(); ++j)
s[i][j] = s[i][j-1+(j==0)] + va[i][j];
}
dfs2(0);
for(int i = 1; i <= n; ++i) {
int x = i; ll t = val[x];
while(x) {
for(int j = 19; ~j; --j)
if(x && t >= f[x][j]) {
t += su[x][j];
x = fa[x][j];
}
if(x) {
int p = std::upper_bound(va[fa[x][0]].begin(), va[fa[x][0]].end(), t) - va[fa[x][0]].begin() - 1;
ll lt = t;
if(p<0) {
t += lt * s[fa[x][0]].size();
t -= Min(val[x], lt);
}
else {
t += lt * (s[fa[x][0]].size()-p-1);
t += s[fa[x][0]][p];
t -= Min(val[x], lt);
}
x = fa[x][0];
}
}
printf("%lld\n", t);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
2020-10-06 分层图 & [JLOI2011]飞行路线题解