Codeforces 1491H - Yuezheng Ling and Dynamic Tree(分块)
*3400 的毒瘤 H 题,特意写个题解纪念一下(
首先对于这种数据结构不太好直接维护的东东可以考虑分块。然鹅我除了分块其他啥也没想到
我们设一个阈值 \(B\),每 \(B\) 个元素分为一块。
我们知道两点之间的 LCA 的大体思想肯定是暴力跳祖先,直到两点重合为止。不过由于此题修改操作的特殊性,什么倍增、树剖都萎掉了。
于是我们考虑一个大致思路:整块暴力跳,散块直接找祖先。
具体来说,我们设 \(pre_i\) 为 \(i\) 的祖先中最大的满足 \(j\) 与 \(i\) 不在同一块中的 \(j\)。
假设我们已经维护好了 \(pre_i\),那么找两点 \(u,v\) 之间的 LCA 这样实现:
- 如果 \(u,v\) 两点不在同一块,那我们就将较靠后的元素(不妨设为 \(u\))跳到 \(pre_u\)
- 如果 \(u,v\) 两点在同一块,且 \(pre_u\ne pre_v\),那我们就令 \(u=pre_u,v=pre_v\)。
- 如果 \(u,v\) 两点在同一块,且 \(pre_u=pre_v\),那我们就令 \(u,v\) 的较大者(不妨设为 \(u\))跳到其父亲为止。
正确性显然,如果你对树剖比较熟应该会理解得深刻一些。这部分复杂度 \(qB+\dfrac{qn}{B}\)。
接下来考虑修改操作,对于边角块,直接暴力修改其 \(a_i\) 值,然后对整块进行重构即可,这样最多只会重构两块,故这部分复杂度为单次询问 \(\mathcal O(B)\)。
对于整块,直接维护比较困难,不过注意到一个性质,那就是进行 \(B\) 次操作之后每个点的 \(fa\) 值都不会属于这个块,也就是说如果某个块的修改次数 \(\geq B\),那么 \(pre_i=fa_i\),故我们维护一个 \(cnt_i\) 表示每个块总共被改了多少次,如果 \(cnt_i\leq B\) 还是暴力重构整个块,否则打一个 \(+x\) 的标记,然后查询某个点的 \(pre\) 值时就返回 \(\max(1,pre_i-tag_i)\),其中 \(tag_i\) 为 \(i\) 所在的块打的标记大小。这部分复杂度为 \(B·\dfrac{n}{B}·B=nB\)
根据均值不等式可知 \(B=\sqrt{n}\) 时最优,总复杂度 \(n\sqrt{n}+q\sqrt{n}\)。
还有一些细节需注意,具体见代码罢:
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=1e5;
const int SQRT=316;
int n,qu,fa[MAXN+5],pre[MAXN+5];
int blk_sz,blk,L[SQRT+5],R[SQRT+5],bel[MAXN+5];
int tag[SQRT+5],sum[SQRT+5];
void update(int x){
for(int i=L[x];i<=R[x];i++) fa[i]=max(1,fa[i]-tag[x]);
tag[x]=0;
for(int i=L[x];i<=R[x];i++){
if(fa[i]<L[x]) pre[i]=fa[i];
else pre[i]=pre[fa[i]];
}
}
int getpre(int x){return max(pre[x]-tag[bel[x]],1);}
int getfa(int x){return max(fa[x]-tag[bel[x]],1);}
void modify(int l,int r,int x){
if(bel[l]==bel[r]){
for(int i=l;i<=r;i++) fa[i]=max(1,fa[i]-x);
update(bel[l]);return;
}
for(int i=l;i<=R[bel[l]];i++) fa[i]=max(1,fa[i]-x);
for(int i=L[bel[r]];i<=r;i++) fa[i]=max(1,fa[i]-x);
update(bel[l]);update(bel[r]);
for(int i=bel[l]+1;i<=bel[r]-1;i++){
tag[i]=min(n,tag[i]+x);sum[i]++;
if(sum[i]<=R[i]-L[i]+1) update(i);
}
}
int getlca(int u,int v){
while(u!=v){
if(bel[u]<bel[v]) swap(u,v);
if(bel[u]>bel[v]) u=getpre(u);
else{
if(getpre(u)==getpre(v)){
while(u!=v){
if(u<v) swap(u,v);
u=getfa(u);
}
} else {
u=getpre(u);v=getpre(v);
}
}
} return u;
}
int main(){
scanf("%d%d",&n,&qu);blk_sz=(int)pow(n,0.5);blk=(n-1)/blk_sz+1;
for(int i=1;i<=blk;i++){
L[i]=(i-1)*blk_sz+1;R[i]=min(i*blk_sz,n);
for(int j=L[i];j<=R[i];j++) bel[j]=i;
}
for(int i=2;i<=n;i++) scanf("%d",&fa[i]);
for(int i=1;i<=blk;i++) update(i);
while(qu--){
int opt;scanf("%d",&opt);
if(opt==1){int l,r,x;scanf("%d%d%d",&l,&r,&x);modify(l,r,x);}
else{int u,v;scanf("%d%d",&u,&v);printf("%d\n",getlca(u,v));}
}
return 0;
}
Codeforces 1491H - Yuezheng Ling and Dynamic Tree(分块)的更多相关文章
- Solution -「CF 1491H」Yuezheng Ling and Dynamic Tree
\(\mathcal{Description}\) Link. 做题原因:题目名. 给定一个长度 \(n-1\) 的序列 \(\{a_2,a_3,\cdots,a_n\}\),其描述了一棵 \ ...
- [cf1491H]Yuezheng Ling and Dynamic Tree
将其按照区间分块(即$[(i-1)K+1,iK]$作为一个块),并定义$f_{x}$表示$x$的祖先中编号最小且与$x$在同一个块内的节点,$f_{x}$可以通过$f_{a_{x}}$转移,即$f_{ ...
- Codeforces 840D Expected diameter of a tree 分块思想
Expected diameter of a tree 我们先两次dfs计算出每个点能到达最远点的距离. 暴力计算两棵树x, y连边直径的期望很好求, 我们假设SZ(x) < SZ(y) 我们枚 ...
- codeforces 1065F Up and Down the Tree
题目链接:codeforces 1065F Up and Down the Tree 题意:给出一棵树的节点数\(n\)以及一次移动的最大距离\(k\),现在有一个标记在根节点1处,每一次可以进行一下 ...
- Codeforces 914H Ember and Storm's Tree Game 【DP】*
Codeforces 914H Ember and Storm's Tree Game 题目链接 ORZ佬 果然出了一套自闭题 这题让你算出第一个人有必胜策略的方案数 然后我们就发现必胜的条件就是树上 ...
- Codeforces Round #499 (Div. 1) F. Tree
Codeforces Round #499 (Div. 1) F. Tree 题目链接 \(\rm CodeForces\):https://codeforces.com/contest/1010/p ...
- Educational Codeforces Round 6 E. New Year Tree dfs+线段树
题目链接:http://codeforces.com/contest/620/problem/E E. New Year Tree time limit per test 3 seconds memo ...
- Codeforces Round #353 (Div. 2) D. Tree Construction 二叉搜索树
题目链接: http://codeforces.com/contest/675/problem/D 题意: 给你一系列点,叫你构造二叉搜索树,并且按输入顺序输出除根节点以外的所有节点的父亲. 题解: ...
- Codeforces Educational Codeforces Round 3 E. Minimum spanning tree for each edge LCA链上最大值
E. Minimum spanning tree for each edge 题目连接: http://www.codeforces.com/contest/609/problem/E Descrip ...
随机推荐
- Stream中的Pipeline理解
使用Stream已经快3年了,但是从未真正深入研究过Stream的底层实现. 今天开始把最近学到的Stream原理记录一下. 本篇文章简单描述一下自己对pipeline的理解. 基于下面一段代码: p ...
- Redis:学习笔记-02
Redis:学习笔记-02 该部分内容,参考了 bilibili 上讲解 Redis 中,观看数最多的课程 Redis最新超详细版教程通俗易懂,来自 UP主 遇见狂神说 4. 事物 Redis 事务本 ...
- 为什么用于开关电源的开关管一般用MOS管而不是三极管
区别: 1.MOS管损耗比三极管小,导通后压降理论上为0. 2.MOS管为电压驱动型,只需要给电压即可,意思是即便串入一个100K的电阻,只要电压够,MOS管还是能够导通. 3.MOS管的温度特性要比 ...
- php swoft redis 发布和订阅
//订阅 public function subscribe() { /* @var \Swoft\Redis\Redis $redis */ $redis = App::getBean(\Swoft ...
- Abp Vnext Vue3 的版本实现
基于ABP Vnext的二次开发,前端 vue3.0,Typescript,Ant Design Vue ,Vben Admin 的后台管理框架. 技术点 Net Core5.0 ABP Vnext ...
- 【AI测试】人工智能 (AI) 测试--开篇
人工智能测试 什么是人工智能,人工智能是怎么测试的.可能是大家一开始最想了解的.大家看图中关于人工智能的定义.通俗点来说呢,就是 让机器实现原来只有人类才能完成的任务:比如看懂照片,听懂说话,思考等等 ...
- 数据库炸了----我就重启了一下啊(Communications link failure)
重启数据库后,数据库大部分时间连不上了:连续请求不会报错,请求间隔时间稍微长一点就会报错报错如图: com.mysql.cj.jdbc.exceptions.CommunicationsExcepti ...
- JDK 之 Arrays.asList - 源码分析
Arrays工具类提供了一个方法asList, 使用该方法可以将一个变长参数或者数组转换成List . 其源代码如下: @SafeVarargs public static <T> Lis ...
- Spring中bean的初始化和销毁几种实现方式
Bean的生命周期 : 创建bean对象 – 属性赋值 – 初始化方法调用前的操作 – 初始化方法 – 初始化方法调用后的操作 – --- 销毁前操作 – 销毁方法的调用. [1]init-metho ...
- CSS学习笔记:flex布局
目录 一.Flex布局简介 1. Flex布局的主要作用 2. Flex布局应用场景 二.Flex布局的使用 1. Flex布局的两种相关元素 2. 父项属性 2.1 flex- direction ...