P8968 觅光 Hard Version 路径压缩 思维 数据结构
因为这里是 1C 的题解不是 2C 的题解,所以就不在叙述博弈的策略与方法了,继续摆出 1C 的结论: 一直向树的上方跳,令目前的答案为 ,另一棵子树的大小是 , 则 。
考虑使用数据结构优化这个过程。
将 分开, ,发现前面那种情况 会倍增,所以最多发生 次,需要特殊处理,其它都是直接加上。
dfs 整棵树的时候用数据结构维护这个操作序列:
, 我们需要找到第一个下标 满足
即
使用线段树维护左边的值,需要支持区间加,二分。
可以在线段树上二分单次 解决,总复杂度是 ,可以过 。
线段树可以维护 “区间加,区间最值”,也可以维护“单点修改,前缀和最值” 。
基于后一种可并的信息 , 可以用倍增维护向上 跳层中不会触发翻倍的最大值,可以做到小常数的 ,场上很多人都卡过去了 。
跳 次的复杂度是扔不掉了,考虑优化跳的过程:
倍增时很多路径段本身是整体的 ,但是被拆成了很多个路径段,每个路径都被拆成了 个,是不优秀的,考虑把这个跳的整段性展现出来,用一种类似并查集路径压缩的思想 :
最初每个点的父亲是自己,如果它一定不会触发父亲的翻倍就把它和父亲压缩到一起,这样每一步都会跳到某段较长的一定不会触发翻倍的段的顶端。
具体实现:
对于每个点维护这个不触发倍增较长段的阈值 ,顶端 ,跳到顶端后需要加的值 ,加入一个新的点后,如果 ,也就是说不触发这个段一定就不会触发上面的段,所以直接连向它上面段的断顶,继续直到段顶满足 。
每经过一个这样的段, 的值至少翻倍(),所以最多经过 个这样的段,直接跳就行了,复杂度 。
部分代码:
struct element {
int limit , addtion , target ;
};
vector< element > Q ;
void dfs(int x) {
if(!x) return ;
int p = (int) Q.size( ) - 1 ;
ans[x] = siz[x] ;
int cnt = 0 ;
while(p > 0) {
auto [limit,addtion,target] = Q[p] ;
++cnt ;
if(ans[x] >= limit) {
ans[x] += addtion ;
p = target ;
continue;
}
ans[x] <<= 1;
p -- ;
}
if(son[x][0])
{
if(son[x][1]) {
auto nw = (element) {siz[son[x][1]],siz[son[x][1]],(int) Q.size( ) - 1} ;
while(nw.limit+nw.addtion >= Q[nw.target].limit) nw.addtion += Q[nw.target].addtion , nw.target = Q[nw.target].target ;
Q.push_back(nw);
}
dfs(son[x][0]) ;
if(son[x][1]) Q.pop_back ( ) ;
}
if(son[x][1]) {
if(son[x][0]) {
auto nw = (element) {siz[son[x][0]],siz[son[x][0]],(int) Q.size( ) - 1} ;
while(nw.limit+nw.addtion >= Q[nw.target].limit) nw.addtion += Q[nw.target].addtion , nw.target = Q[nw.target].target ;
Q.push_back(nw);
}
dfs(son[x][1]) ;
if(son[x][0]) Q.pop_back ( ) ;
}
}
于 1 月 25 日排在最优解第二。
收录于《超级无敌神仙炫酷无敌原神大王好题》 。
本文已经结束了。本文作者:ღꦿ࿐(DeepSea),转载请注明原文链接:https://www.cnblogs.com/Dreamerkk/p/17096128.html,谢谢你的阅读或转载!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2022-02-06 atcoder近期比赛记录