Codeforces 题目传送门 & 洛谷题目传送门

*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(分块)的更多相关文章

  1. Solution -「CF 1491H」Yuezheng Ling and Dynamic Tree

    \(\mathcal{Description}\)   Link. 做题原因:题目名.   给定一个长度 \(n-1\) 的序列 \(\{a_2,a_3,\cdots,a_n\}\),其描述了一棵 \ ...

  2. [cf1491H]Yuezheng Ling and Dynamic Tree

    将其按照区间分块(即$[(i-1)K+1,iK]$作为一个块),并定义$f_{x}$表示$x$的祖先中编号最小且与$x$在同一个块内的节点,$f_{x}$可以通过$f_{a_{x}}$转移,即$f_{ ...

  3. Codeforces 840D Expected diameter of a tree 分块思想

    Expected diameter of a tree 我们先两次dfs计算出每个点能到达最远点的距离. 暴力计算两棵树x, y连边直径的期望很好求, 我们假设SZ(x) < SZ(y) 我们枚 ...

  4. codeforces 1065F Up and Down the Tree

    题目链接:codeforces 1065F Up and Down the Tree 题意:给出一棵树的节点数\(n\)以及一次移动的最大距离\(k\),现在有一个标记在根节点1处,每一次可以进行一下 ...

  5. Codeforces 914H Ember and Storm's Tree Game 【DP】*

    Codeforces 914H Ember and Storm's Tree Game 题目链接 ORZ佬 果然出了一套自闭题 这题让你算出第一个人有必胜策略的方案数 然后我们就发现必胜的条件就是树上 ...

  6. Codeforces Round #499 (Div. 1) F. Tree

    Codeforces Round #499 (Div. 1) F. Tree 题目链接 \(\rm CodeForces\):https://codeforces.com/contest/1010/p ...

  7. 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 ...

  8. Codeforces Round #353 (Div. 2) D. Tree Construction 二叉搜索树

    题目链接: http://codeforces.com/contest/675/problem/D 题意: 给你一系列点,叫你构造二叉搜索树,并且按输入顺序输出除根节点以外的所有节点的父亲. 题解: ...

  9. 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 ...

随机推荐

  1. Object.create 和 Object.assign

    Object.assign(target, ...source) 1.Object.assign方法只会拷贝源对象自身(不包括原型)的并且可枚举的属性到目标对象,使用源对象的get和目标对象的set, ...

  2. ffmpeg剪视频

    ffmpeg裁剪合并视频   ffmpeg提供简单的命令参数: ffmpeg -ss START -t DURATION -i INPUT -vcodec copy -acodec copy OUTP ...

  3. udev 使用方法

    原文地址 http://blog.163.com/againinput4@yeah/blog/static/122764271200962305339483/ 最近有在研究SD卡设备节点自动创建及挂载 ...

  4. RabbitMQ多消费者顺序性消费消息实现

    最近起了个项目消息中心,用来中转各个系统中产生的消息,用到的是RabbitMQ,由于UAT环境.生产环境每台消费者服务都是多台,有些消息要求按顺序消费,所以需要采取一定的措施保证消息的顺序消费,下面讲 ...

  5. k8s入坑之路(13)服务迁移(定时任务 微服务 传统服务)

    定时任务迁移kubernetes 服务迁移步骤 1.安装好java 2.安装好maven 项目打包 mvn package 测试传参运行 java -cp cronjob-demo-1.0-SNAPS ...

  6. jacoco-统计代码覆盖率并生成报告

    一.概述: 作为一个合格的测试人员,保证产品的软件质量是其工作首要目标,为了这个目标,测试人员常常会通过很多手段或工具来加以保证,覆盖率就是其中一环比较重要的环节. 通常我们会将测试覆盖率分为两个部分 ...

  7. 手把手教你学Dapr - 5. 状态管理

    上一篇:手把手教你学Dapr - 4. 服务调用 介绍 使用状态管理,您的应用程序可以将数据作为键/值对存储在支持的状态存储中. 您的应用程序可以使用 Dapr 的状态管理 API 使用状态存储组件来 ...

  8. RISCV 入门 (学习笔记)

    文章目录 1. risv 相关背景 1.1 arm 授权费 1.2 riscv 发展历史 1.3 riscv 风险 2. 指令集 2.1 可配置的通用寄存器组 2.2 规整的指令编码 2.3 简洁的存 ...

  9. 从0到1使用Kubernetes系列(七):网络

    本文是从 0 到 1 使用 Kubernetes 系列第七篇,上一篇<从 0 到 1 使用 Kubernetes 系列(六):数据持久化实战> 介绍了 Kubernetes 中的几种常用储 ...

  10. IDEA 设置Java项目使用的JDK版本 最全篇

    1. File -> Project Setting -> Project : 2. File ->Project Setting -> Modules 3. File -&g ...