Loading

题解: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;
}
posted @ 2026-03-27 14:02  GE9x  阅读(11)  评论(0)    收藏  举报