题解:AT_abc417_g [ABC417G] Binary Cat
非常好的一道 ad-hoc。
显然这是一个树状物,很快就可以想到暴力算法:设计递归函数 \(solve(u,len)\) 代表 \(s_u\) 的第 \(len\) 个字符是啥,递归是简单的,其实就是一个树上搜索的问题。
很快你会发现斐波那契是指数级上升的,如果每一个 \(s_l\),\(s_r\) 的长度相等,那么最多只需要 \(\log\) 次跳跃。那么考虑类似于启发式合并的东西,如果一直往较短的那个儿子跳,那么最多需要 \(\log\) 次。然后这些跳“短儿子”的操作会把整个跳跃过程分为 \(\log\) 段,不包含端点的话,这 \(\log\) 段其实就是只包含往较长的儿子的跳跃。然后只保留这些往较长儿子的边,这棵树就会分成很多条链,直接对这些链倍增预处理即可,这样就是两只老哥,6 秒绰绰有余。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e5 + 7, inf = 1e18 + 1;
int n, l[N], r[N], x[N], len[N];
int to[N][30], sum[N][30], pre[N][30];
signed main(){
ios::sync_with_stdio(0), cin.tie(0);
cin >> n;
len[0] = len[1] = 1;
for(int i = 0; i <= n + 3; i ++)
for(int j = 0; j < 30; j ++) to[i][j] = pre[i][j] = n + 3;
for(int i = 2; i <= n + 1; i ++){
cin >> l[i] >> r[i] >> x[i];
len[i] = len[l[i]] + len[r[i]];
len[i] = min(len[i], inf);
to[i][0] = (len[l[i]] >= len[r[i]] ? l[i] : r[i]);
sum[i][0] = (len[l[i]] >= len[r[i]] ? 0 : len[l[i]]);
pre[i][0] = l[i];
for(int j = 1; j <= 20; j ++){
pre[i][j] = pre[pre[i][j - 1]][j - 1];
to[i][j] = to[to[i][j - 1]][j - 1];
sum[i][j] = sum[i][j - 1] + sum[to[i][j - 1]][j - 1];
}
}
for(int i = 2; i <= n + 1; i ++){
int u = i, wh = x[i];
for(int j = 20; j >= 0; j --){
if(pre[u][j] <= u && len[pre[u][j]] >= x[i]) u = pre[u][j];
}
while(u > 1){
for(int j = 20; j >= 0; j --)
if(to[u][j] <= u && sum[u][j] < wh && wh - sum[u][j] <= len[to[u][j]]) wh -= sum[u][j], u = to[u][j];
if(u > 1){
if(len[l[u]] < wh) wh -= len[l[u]], u = r[u];
else u = l[u];
}
}
cout << u << "\n";
}
return 0;
}
本文来自博客园,作者:GE9x,转载请注明原文链接:https://www.cnblogs.com/GE9X/p/19781810

浙公网安备 33010602011771号