题面



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模拟赛】校门外歪脖树上的鸽子(树链剖分)的更多相关文章

  1. 【NOI P模拟赛】最短路(树形DP,树的直径)

    题面 给定一棵 n n n 个结点的无根树,每条边的边权均为 1 1 1 . 树上标记有 m m m 个互不相同的关键点,小 A \tt A A 会在这 m m m 个点中等概率随机地选择 k k k ...

  2. jzoj5987. 【WC2019模拟2019.1.4】仙人掌毒题 (树链剖分+概率期望+容斥)

    题面 题解 又一道全场切的题目我连题目都没看懂--细节真多-- 先考虑怎么维护仙人掌.在线可以用LCT,或者像我代码里先离线,并按时间求出一棵最小生成树(或者一个森林),然后树链剖分.如果一条边不是生 ...

  3. BZOJ2040[2009国家集训队]拯救Protoss的故乡——模拟费用流+线段树+树链剖分

    题目描述 在星历2012年,星灵英雄Zeratul预测到他所在的Aiur行星在M天后会发生持续性暴雨灾害,尤其是他们的首都.而Zeratul作为星灵族的英雄,当然是要尽自己最大的努力帮助星灵族渡过这场 ...

  4. [BZOJ2164]采矿【模拟+树链剖分+线段树】

    Online Judge:Bzoj2164 Label:模拟,树链剖分,线段树 题目描述 浩浩荡荡的cg大军发现了一座矿产资源极其丰富的城市,他们打算在这座城市实施新的采矿战略.这个城市可以看成一棵有 ...

  5. 2016湖南省赛 I Tree Intersection(线段树合并,树链剖分)

    2016湖南省赛 I Tree Intersection(线段树合并,树链剖分) 传送门:https://ac.nowcoder.com/acm/contest/1112/I 题意: 给你一个n个结点 ...

  6. [模拟]P1047 校门外的树

    校门外的树 题目描述 某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米.我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置:数轴上的每个整数点,即0,1,2,- ...

  7. [NOI P模拟赛] 传统艺能(子序列自动机、矩阵乘法,线段树)

    (2:00)OID:"完了,蓝屏了!"(代码全消失) 众人欢呼 OID:开机,"原题测试--" (30min later)OID 开始传统艺能: " ...

  8. noi.acNOIP模拟赛5-count

    题目链接 戳我 题意简述 你有一个n+1个数的序列,都是1~n,其中只有一个有重复,求每个长度的本质不同的子序列个数.\(mod 1e9+7\). sol 说起来也很简单,设相同的数出现的位置为\(l ...

  9. 【XJOI】【NOI考前模拟赛7】

    DP+卡常数+高精度/  计算几何+二分+判区间交/  凸包 首先感谢徐老师的慷慨,让蒟蒻有幸膜拜了学军的神题.祝NOI2015圆满成功 同时膜拜碾压了蒟蒻的众神QAQ 填填填 我的DP比较逗比……( ...

随机推荐

  1. camunda如何调用HTTP REST(Service Task)服务节点

    ​ Camunda中的Service Task(服务任务)用于调用服务.在Camunda中,可以通过调用本地Java代码.外部工作项.web服务形式实现的逻辑来完成的. 本文重点描述如何使用web服务 ...

  2. 『忘了再学』Shell基础 — 32、Shell中test测试命令详解

    目录 1.test测试命令 (1)test命令介绍 (2)test命令使用方式 (3)示例 2.按照文件类型进行判断 3.按照文件权限进行判断 4.两个文件之间进行比较 5.两个整数之间比较 6.字符 ...

  3. MySQL-5-TCL,视图,变量,存储过程和函数,流程控制

    TCL:Transaction Control Language事务控制语言 TCL 事务的特点 acid: 原子性(Atomicity),一致性(Consistency),隔离性(isolation ...

  4. UiPath培训教程

    匠厂出品,必属精品   Uipath中文社区qq交流群:465630324 uipath中文交流社区:https://uipathbbs.comRPA之家qq群:465620839 第一课--UiPa ...

  5. Python列表解析式的正确使用方式(一)

    先来逼逼两句: Python 是一种极其多样化和强大的编程语言!当需要解决一个问题时,它有着不同的方法.在本文中,将会展示列表解析式 (List Comprehension).我们将讨论如何使用它?什 ...

  6. 聊聊 Netty 那些事儿之 Reactor 在 Netty 中的实现(创建篇)

    本系列Netty源码解析文章基于 4.1.56.Final版本 在上篇文章<聊聊Netty那些事儿之从内核角度看IO模型>中我们花了大量的篇幅来从内核角度详细讲述了五种IO模型的演进过程以 ...

  7. STC8H开发(十二): I2C驱动AT24C08,AT24C32系列EEPROM存储

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

  8. MyBatis项目创建

    一.开发环境的准备 总览: mybatis搭建过程: 1.导入jar 2.创建mybatis的核心(全局)配置文件mybatis-config.xml,并配置 3.创建映射文件XxxMapper.xm ...

  9. labview从入门到出家7(进阶篇)--队列的使用

    本节简单讲解队列在Labview中的使用,队列你可以认为就是一组先进先出的数据列表,在Labview中常用来缓存和传递数据.用了这么久的队列,个人认为有个方便的地方在于数据传递的把控,不管是局部变量还 ...

  10. 【C++】学生管理系统

    [C++]学生管理系统 一道非常经典的C语言题目,用C++实现   题目如下: 输入功能:由键盘输入10个学生的学号.姓名.三科成绩,并计算出平均成绩和总成绩,然后将它存入文件stud.dat. 插入 ...