【NOI P模拟赛】校门外歪脖树上的鸽子(树链剖分)
题面
2
≤
n
≤
2
×
1
0
5
,
1
≤
m
≤
2
×
1
0
5
,
1
≤
l
≤
r
≤
n
,
1
≤
d
≤
1
0
8
2 ≤ n ≤ 2 × 10^5,1 ≤ m ≤ 2 × 10^5,1 ≤ l ≤ r ≤ n,1 ≤ d ≤ 10^8
2≤n≤2×105,1≤m≤2×105,1≤l≤r≤n,1≤d≤108
题解
其实核心思路很简单:我们把原树看成一棵zkw线段树,而不是普通线段树,这样我们修改以及询问的对象就是左哨兵链的右兄弟 以及 右哨兵链的左兄弟,这两条链从叶子一直延申到 lca 的孙子辈。
树链剖分分别维护记录每个点 右兄弟 和 左兄弟 的两棵线段树,时间复杂度
O
(
(
n
+
m
)
log
2
n
)
O((n+m)\log^2n)
O((n+m)log2n)。
剩下的就是一些细节了。
首先预处理两棵线段树的基本信息,需要让左儿子记录右儿子的区间长度,右儿子记录左儿子的区间长度,然后再根据子树大小(区间长度)重链剖分。剖分后保留原先记录的区间长度不变,也就是说原先的右儿子可能 dfs 序还比左儿子小,但是仍然记录原先左儿子的区间长度、在“左兄弟”线段树上做贡献,并扣牢“右儿子”的帽子。修改以及询问的时候,不因为左右端点实际 dfs 序的颠倒而互换。
针对左右边界,可以加两个哨兵,也可以直接特判。
控制访问到 lca 的孙子辈,比较难且常数较大,我们可以记录儿子辈,然后访问完后把儿子辈的修改 / 贡献撤回。
CODE
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<random>
#include<vector>
#include<bitset>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 400005
#define LL long long
#define ULL unsigned long long
#define DB double
#define lowbit(x) (-(x) & (x))
#define ENDL putchar('\n')
#define FI first
#define SE second
LL read() {
LL f=1,x=0;int s = getchar();
while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
while(s >= '0' && s <= '9') {x = (x<<3) + (x<<1) + (s^48); s = getchar();}
return f*x;
}
LL read(int first_c) {
LL f=1,x=0;int s = first_c;
while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
while(s >= '0' && s <= '9') {x = (x<<3) + (x<<1) + (s^48); s = getchar();}
return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar('0'+(x%10));}
void putnum(LL x) {
if(!x) {putchar('0');return ;}
if(x<0) {putchar('-');x = -x;}
return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}
int n,m,s,o,k;
int ls[MAXN],rs[MAXN];
int l[MAXN],r[MAXN];
int fa[MAXN];
int d[MAXN],dfn[MAXN],rr[MAXN],tp[MAXN],tim,id[MAXN];
int lw[MAXN],rw[MAXN];
void dfs0(int x) {
d[x] = d[fa[x]] + 1;
tim = 0;
if(!ls[x]) return ;
dfs0(ls[x]); dfs0(rs[x]);
if(l[ls[x]] > l[rs[x]]) swap(ls[x],rs[x]);
l[x] = l[ls[x]]; r[x] = r[rs[x]];
int A,B;
A = lw[rs[x]] = r[ls[x]] - l[ls[x]] + 1;
B = rw[ls[x]] = r[rs[x]] - l[rs[x]] + 1;
if(A < B) swap(ls[x],rs[x]);
return ;
}
void dfs(int x) {
if(x == ls[fa[x]]) tp[x] = tp[fa[x]];
else tp[x] = x;
dfn[x] = ++ tim; id[tim] = x;
if(ls[x]) dfs(ls[x]);
if(rs[x]) dfs(rs[x]);
rr[x] = tim;
return ;
}
struct it{
int ls,rs;
int ct;
LL sm,lz;
it(){ls=rs=ct=sm=lz=0;}
}tre[MAXN<<2];
int cnt;
void addd(int a,LL d) {
tre[a].sm += tre[a].ct *1ll* d;
tre[a].lz += d; return ;
}
void update(int a) {
tre[a].sm = tre[tre[a].ls].sm + tre[tre[a].rs].sm;
tre[a].ct = tre[tre[a].ls].ct + tre[tre[a].rs].ct;
return ;
}
void pushdown(int a) {
if(tre[a].lz) {
addd(tre[a].ls,tre[a].lz);
addd(tre[a].rs,tre[a].lz);
tre[a].lz = 0;
}return ;
}
int maketree(int *p,int l,int r) {
int a = ++ cnt; tre[a] = it();
if(l == r) {tre[a].sm = 0; tre[a].ct = p[id[l]];}
else {
int md = (l + r) >> 1;
tre[a].ls = maketree(p,l,md);
tre[a].rs = maketree(p,md+1,r);
update(a);
}
return a;
}
void addtree(int a,int l,int r,int al,int ar,int d) {
if(l > r || al > r || ar < l) return ;
if(al >= l && ar <= r) {
addd(a,d); return ;
}
int md = (al + ar) >> 1; pushdown(a);
addtree(tre[a].ls,l,r,al,md,d); addtree(tre[a].rs,l,r,md+1,ar,d);
update(a); return ;
}
LL findtree(int a,int l,int r,int al,int ar) {
if(l > r || al > r || ar < l) return 0ll;
if(al >= l && ar <= r) return tre[a].sm;
int md = (al + ar) >> 1; pushdown(a);
return findtree(tre[a].ls,l,r,al,md) + findtree(tre[a].rs,l,r,md+1,ar);
}
LL FL = 0;
int RT1,RT2;
void addline(int a,int b,int y) {
if(a < 1 && b > n) {
FL += y *1ll* n; return ;
}
if(a < 1) {
while(b) {
addtree(RT1,dfn[tp[b]],dfn[b],1,tim,y);
b = fa[tp[b]];
} return ;
}
if(b > n) {
while(a) {
addtree(RT2,dfn[tp[a]],dfn[a],1,tim,y);
a = fa[tp[a]];
} return ;
}
int ld = 0,rd = 0;
while(tp[a] != tp[b]) {
if(d[tp[a]] > d[tp[b]]) {
addtree(RT2,dfn[tp[a]],dfn[a],1,tim,y); ld = tp[a];
a = fa[tp[a]];
}
else {
addtree(RT1,dfn[tp[b]],dfn[b],1,tim,y); rd = tp[b];
b = fa[tp[b]];
}
}
if(d[a] <= d[b]) addtree(RT2,dfn[ld],dfn[ld],1,tim,-y);
if(d[b] <= d[a]) addtree(RT1,dfn[rd],dfn[rd],1,tim,-y);
if(d[a] < d[b]) addtree(RT1,dfn[a]+2,dfn[b],1,tim,y);
if(d[b] < d[a]) addtree(RT2,dfn[b]+2,dfn[a],1,tim,y);
return ;
}
LL findline(int a,int b) {
if(a < 1 && b > n) {
return FL;
}
LL ans = 0;
if(a < 1) {
while(b) {
ans += findtree(RT1,dfn[tp[b]],dfn[b],1,tim);
b = fa[tp[b]];
} return ans;
}
if(b > n) {
while(a) {
ans += findtree(RT2,dfn[tp[a]],dfn[a],1,tim);
a = fa[tp[a]];
} return ans;
}
int ld = 0,rd = 0;
while(tp[a] != tp[b]) {
if(d[tp[a]] > d[tp[b]]) {
ans += findtree(RT2,dfn[tp[a]],dfn[a],1,tim); ld = tp[a];
a = fa[tp[a]];
}
else {
ans += findtree(RT1,dfn[tp[b]],dfn[b],1,tim); rd = tp[b];
b = fa[tp[b]];
}
}
if(d[a] <= d[b]) ans -= findtree(RT2,dfn[ld],dfn[ld],1,tim);
if(d[b] <= d[a]) ans -= findtree(RT1,dfn[rd],dfn[rd],1,tim);
if(d[a] < d[b]) ans += findtree(RT1,dfn[a]+2,dfn[b],1,tim);
if(d[b] < d[a]) ans += findtree(RT2,dfn[b]+2,dfn[a],1,tim);
return ans;
}
int main() {
freopen("pigeons.in","r",stdin);
freopen("pigeons.out","w",stdout);
n = read();m = read();
for(int i = 1;i <= n;i ++) l[i] = r[i] = i;
for(int i = n+1;i < (n<<1);i ++) {
s = ls[i] = read();
o = rs[i] = read();
fa[s] = fa[o] = i;
}
int rt = 0;
for(int i = n+1;i < (n<<1);i ++) {
if(!fa[i]) rt = i;
}
dfs0(rt);
dfs(rt);
RT1 = maketree(lw,1,tim);
RT2 = maketree(rw,1,tim);
for(int i = 1;i <= m;i ++) {
k = read();
if(k == 1) {
s = read();o = read();k = read();
addline(s-1,o+1,k);
}
else {
s = read();o = read();
AIput(findline(s-1,o+1),'\n');
}
}
return 0;
}
【NOI P模拟赛】校门外歪脖树上的鸽子(树链剖分)的更多相关文章
- 【NOI P模拟赛】最短路(树形DP,树的直径)
题面 给定一棵 n n n 个结点的无根树,每条边的边权均为 1 1 1 . 树上标记有 m m m 个互不相同的关键点,小 A \tt A A 会在这 m m m 个点中等概率随机地选择 k k k ...
- jzoj5987. 【WC2019模拟2019.1.4】仙人掌毒题 (树链剖分+概率期望+容斥)
题面 题解 又一道全场切的题目我连题目都没看懂--细节真多-- 先考虑怎么维护仙人掌.在线可以用LCT,或者像我代码里先离线,并按时间求出一棵最小生成树(或者一个森林),然后树链剖分.如果一条边不是生 ...
- BZOJ2040[2009国家集训队]拯救Protoss的故乡——模拟费用流+线段树+树链剖分
题目描述 在星历2012年,星灵英雄Zeratul预测到他所在的Aiur行星在M天后会发生持续性暴雨灾害,尤其是他们的首都.而Zeratul作为星灵族的英雄,当然是要尽自己最大的努力帮助星灵族渡过这场 ...
- [BZOJ2164]采矿【模拟+树链剖分+线段树】
Online Judge:Bzoj2164 Label:模拟,树链剖分,线段树 题目描述 浩浩荡荡的cg大军发现了一座矿产资源极其丰富的城市,他们打算在这座城市实施新的采矿战略.这个城市可以看成一棵有 ...
- 2016湖南省赛 I Tree Intersection(线段树合并,树链剖分)
2016湖南省赛 I Tree Intersection(线段树合并,树链剖分) 传送门:https://ac.nowcoder.com/acm/contest/1112/I 题意: 给你一个n个结点 ...
- [模拟]P1047 校门外的树
校门外的树 题目描述 某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米.我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置:数轴上的每个整数点,即0,1,2,- ...
- [NOI P模拟赛] 传统艺能(子序列自动机、矩阵乘法,线段树)
(2:00)OID:"完了,蓝屏了!"(代码全消失) 众人欢呼 OID:开机,"原题测试--" (30min later)OID 开始传统艺能: " ...
- noi.acNOIP模拟赛5-count
题目链接 戳我 题意简述 你有一个n+1个数的序列,都是1~n,其中只有一个有重复,求每个长度的本质不同的子序列个数.\(mod 1e9+7\). sol 说起来也很简单,设相同的数出现的位置为\(l ...
- 【XJOI】【NOI考前模拟赛7】
DP+卡常数+高精度/ 计算几何+二分+判区间交/ 凸包 首先感谢徐老师的慷慨,让蒟蒻有幸膜拜了学军的神题.祝NOI2015圆满成功 同时膜拜碾压了蒟蒻的众神QAQ 填填填 我的DP比较逗比……( ...
随机推荐
- 开源流程引擎Camunda BPM如何扩展数据库表
前言 在使用开源流程引擎(如:JBPM.Activiti.Flowable.Camunda等)的时候,经常会遇到这样的需求,我们需要按照业务需求增加一张数据库的表,而且这张表是跟工作流引擎有交互的(注 ...
- ubuntu下连microsoft sql server解决方案
shell for MSSQL: https://github.com/dbcli/mssql-cli mssql-cli -S 127.0.0.1,1433 -d testDB -U myuser ...
- XAMPP Apache安装时问题总结
昨天遇到的一个问题,安装XAMPP后无法启动Apache服务,本以为可能是一些系统服务文件被占用的原因,安装软件后需重启一遍.可是重启计算机后依然无法启动Apache服务,状态栏里总是显示" ...
- Python快速下载商品数据,并连接数据库,保存数据
开发环境 python 3.8 pycharm 2021.2 专业版 代码实现 发送请求 获取数据 解析数据(筛选数据) 保存数据 连接数据库 开始代码 请求数据 # 伪装 headers = { ' ...
- Spring框架系列(9) - Spring AOP实现原理详解之AOP切面的实现
前文,我们分析了Spring IOC的初始化过程和Bean的生命周期等,而Spring AOP也是基于IOC的Bean加载来实现的.本文主要介绍Spring AOP原理解析的切面实现过程(将切面类的所 ...
- 广义径向基网络(RBF网络)
- 记录自己NVIDIA GeForce MX250迷之安装cuda+pytorch成功了
电脑是ubuntu20.4 Pop!_OS 20.04 LTS MX250显卡并没有列在CUDA支持的GPU里 希望文中链接的别人的博客不会消失掉. 安装了英伟达的驱动 参考了这一篇:Ubuntu 安 ...
- 分享|2022数字安全产业大数据白皮书(附PDF)
内容摘要: 2021年以来,数字安全赛道的受关注程度达到一个历史新高度.<数据安全法><个人信息保护法><关键信息基础设施安全保护条例>,一个接一个重磅的法规接连出 ...
- YII http缓存
http禁止缓存原理 header('Expires: 0'); header('Last-Modified: '. gmdate('D, d M Y H:i:s') . ' GMT'); heade ...
- 手搓一个“七夕限定”,用3D Engine 5分钟实现烟花绽放效果
七夕来咯!又到了给重要的人送惊喜的时刻. 今年,除了将心意融入花和礼物,作为程序员,用自己的代码本事手搓一个技术感十足"七夕限定"惊喜,我觉得,这是不亚于车马慢时代手写信的古典主义 ...