集训(一)
2017.11.25 ~ 2017.12.07
一息尚在,安敢回头
(好题标粗辣!
// 一段话之前有 ‘TAT’ 的是直接粘的 PPT 上的,下面 ‘(’ 中会有写自己的解释
// 如果网上找不到的题或者是很难找到的题会放上题意,否则会给出题号
11.28
下午敦敦敦来讲数据结构!感觉很多题都很不可做啊
(准备好好看一看课件 qwq
// 敦敦敦的题都十分巧妙啊!都是好题呀!
序列
题意:给定一个长度为
n
的序列a[1 .. n]
,定义f(l, r) = a[l .. r]
中的最小值
有 Q 次询问,每次给出l, r
,求 ∑l≤i≤j≤rf(i,j)
1≤n,Q≤105,0≤|ai|≤106TAT (全局
先考虑当区间是[1, n]
时怎么做
这是个经典题
设cl[i]
为最大的j
满足a[j] ≤ a[i]
,若不存在则为0
设cr[i]
为最小的j
满足a[j] < a[i]
,若不存在则设为右边界+ 1
(即n + 1
答案就是 ∑ni=1a[i]∗(i−cl[i])∗(cr[i]−i)
(必须满足cl[i] ≤ i < cr[i]
(我们找到
cl[i]
与cr[i]
以后,那么在cl[i] ~ cr[i]
这个区间中的最小值就是a[i]
,所以每个i
的贡献都是 a[i]∗(i−cl[i])∗(cr[i]−i) ,求个和即可。
(还要注意cl[i] ~ i
和i ~ cr[i]
这两个区间内,必须是有一个取到等号,一个不取等号,否则会算重。
什么不理解?那我们模拟一下好了 …1 2 2 cl[1]=0 cr[1]=4 cl[2]=1 cr[2]=4 cl[3]=2 cr[3]=4 ans = 1 * 1 * 3 + 2 * 1 * 2 + 2 * 1 * 1 = 9; // 左区间取到等号,右区间没取到 cl[1]=0 cr[1]=4 cl[2]=1 cr[2]=4 cl[3]=1 cr[3]=4 ans = 1 * 1 * 3 + 2 * 1 * 2 + 3 * 2 * 1 = 11; // 都取到了等号
TAT (区间询问
接下来考虑区间询问
显然就算取的是一个区间,cl[i]
和cr[i]
也是不变的,顶多会超出边界
对答案的贡献是 a[i]∗(i−max(l−1,cl[i]))∗(min(r+1,cr[i])−i)TAT (分类讨论
当l >= cl[i] + 1, r <= cr[i] - 1
时,贡献系数是 (r−i)∗(i−l)=−rl+(r+l)i−i2
以此类推,最后可以发现,对于一个区间(l,r)
他的答案可以表示成 k1∗(rl)+k2∗r+k3∗l+k4
考虑离线,从左往右枚举r
,r
固定时i
的贡献只有两种,分别针对l ∈ [1, cl[i] + 1]
和l ∈ [c[i] + 2, n]
,分段加一下,
然后当r
到了某个值后有些 i 的贡献还会改变
用线段树维护那个四元组,时间复杂度: O((n+Q)logn)segment tree
题意:给定
n
,定义f(l, r)
为在线段树上对(l, r)
进行区间求和需要经过的结点的个数
有 Q 次询问,每次询问 ∑L≤l≤r≤Rf(l,r)
1≤n,Q≤105TAT (做法
访问到这个结点等价于包含这个结点的区间但不包含父亲的区间
用线段树维护即可
时间复杂度: O((n+Q)logn)【Wannafly 挑战赛 2】D - Delete 题解(这道题的题解在最后
题意:给定一个
n
个点,m
条边的带权有向无环图,同时给定起点S
和终点T
。
一共有Q
个询问,每次询问删掉某个点和所有与这个点相连的边之后S
到T
的最短路的长度,
询问之间互相独立(也就是删除操作在询问结束之后会立即撤销)
1≤n,q≤105 , 1≤m≤2∗105 ,边权的绝对值 ≤109(我们首先要注意一个看上去没有那么有用的信息 … 恩它是一个有向无环图 … 所以我们要想到拓扑序
TAT (拓扑序
这题有个特点,就是它是一个有向无环图,那么我们可以对他求拓扑序
可以发现对于一条路径,它经过的点的拓扑序一定是单调增加的
当我们删除了某个点x
后,相当于在拓扑序中删除了这个点,然后求最短路(因为拓扑序,所以删除一个点
u
后,一定有边u
的左边连到u
的右边,枚举所有这样的边(x, y, w)
,用dist(S, x) + w + dist(y, T)
更新拓扑序中区间(x, y)
的最小值。查询用线段树维护即可。时间复杂度: O(nlogn+Q)
【BZOJ 3306】树 题解
dfs
序对应的区间就是子树,这样就能用线段树维护。如果没有换根的话,就是用线段树维护
dfs
序,然后单点修改,区间询问。有换根其实就是在没有换根的基础上分类讨论一下就好。
我们进行换根操作就是直接让root
等于要换的那个节点。最开始的时候树以1
为根。
分类讨论具体请看上面那个题解 ↑。这里说一下当
lca(x, root) = x
时,这时从以1
为根的树中看,root
在x
子树内。找到x
到root
路径中最接近x
的结点y
,然后查询的时候踢开以y
为根的子树范围查询。
为什么?
因为你换根是直接赋的值啊 … 这样做正确性显然辣。【BZOJ 4383】 【POI 2015】Pustynia 题解
建一张拓扑图,如果
x
必须比y
大,则x
向y
连边,最后如果有环则无解,否则任意求个拓扑序就是答案。这样做边数是 O(N2) 的,考虑优化建图。接下来有一个套路:用数据结构优化建图。
对于每条信息,我们新建一个虚拟结点
now
。
对于[l, r]
中所有给出的较大的位置x[i]
,now->x[i]
边权为0
。
所有[l, r]
中除x
以外的位置y[i]
,利用线段树优化建图,x[i]
把区间[l, r]
分成了好几段,每次连边的时候直接让线段树中对应的区间(l1, r1)
连向now
,边权为1
。
也就是 (l1,r1)−−1−−>now−−0−−>x[i]
总边数 O(n+∑k∗logn)因为很多点已经给出了权值,那么
f[i]
应该对a[i]
取max
,如果f[x] > a[x]
,无解。
如果有某个点的权值超过 109 ,无解。
如果图中存在环,无解。and
题意:给定一个序列
a[1..n]
和Q
次操作,操作有三种:
C l r x:将a[l..r]
全部改为 x
S l r:求 ∑ri=la[i]
A l r x:令a[l..r]
中每个数都and
上x
1≤n,Q≤105 , 0≤a[i],x≤109TAT(问题转化
首先,根据位运算的独立性,可以位与位之间分别维护,这样就花费了 O(loga[i]) 的代价使得x
变成了0
或1
(就是建三十个线段树 … 按位建,每一位分开来处理
为什么(至少)是三十个线段树?
因为 0≤a[i],x≤109 , 109 的二进制位有三十位。TAT(问题转化
于是 C 操作就是区间覆盖
S 就是区间求和
A 操作:若x = 1
则啥都不干,否则也是区间覆盖
区间覆盖区间求和可以直接线段树维护,时间复杂度: O((N+Q)log2n)【UOJ 164】【清华集训 2015】V 题解
对于修改操作,我们打标记
(a, b)
,代表对于该点维护的区间,执行这个标记,对于区间内的数,都要先加上a
然后与b
取max
。那么对于操作1、2、3
,分别对应(x, - inf),(- x, 0),(- inf, x)
。考虑标记如何下传,显然这个标记是可以下传的,即
(a + A, max(b + A, B))
。对于询问单点历史最值,要注意我们需要记录的是历史最大标记而不是直接在每个点记录历史最大值。
为什么?
因为假设我们进行一次区间赋为inf
的操作,接着又全部赋为0
,标记还没来得及下传更新历史最大值就被后一个标记覆盖了,所以每个点记录历史最大值是错的。ball
题意:有一个正整数序列
a[1..n]
和m
个区间[l, r]
有 Q 次操作,每次让a[x] −= y
,保证每时每刻a[x] >= 0
每次操作过后,你需要回答初始给定的m
个区间里,有几个满足区间和为0
1≤n,m,q≤105TAT (做法
对a[1..n]
建线段树,于是每个区间可以看成线段树上 O(logn) 个结点
一个区间的和为0
等价于这 O(logn) 个结点的和都为0
每次单点修改后如果有新的结点和变成0
了就暴力枚举下包含这个结点的区间
时间复杂度: O((n+Q+m)logn)【BZOJ 3702 / 2212】【Poi 2011】Tree Rotations 题解
权值线段树和区间线段树的唯一区别就是叶子节点的存储信息,区间所维护的信息。
子树
x
内的逆序对个数为 :x
左子树内的逆序对个数 +x
右子树内的逆序对个数 + 跨越x
左子树与右子树的逆序对。左右子树内部的逆序对与是否交换左右子树无关,因为子树内的顺序对子树之间的贡献是没有影响的。是否交换左右子树取决于交换后 “跨越
x
左子树与右子树的逆序对” 是否会减小。因此我们要求出是否交换这两种情况下跨越 x 左子树与右子树的逆序对,对每个叶子节点建一个权值线段树,对每个非叶子节点合并他的两个儿子节点的权值线段树,合并的时候算出逆序对数,用其中较小的一个更新答案。而不需要真正的去交换子树。
时间复杂度: O(nlogn)【BZOJ 4408】【Fjoi 2016】神秘数 题解
如果存在一个集合,使得
[1, x - 1]
内的数字都能被表示,此时神秘数为x
新加入一个数y
,那么会出现如下两种情况:
1.y ≤ x
,则新集合可以表示[1, x - 1 + y]
内的所有数字。
2.y > x
,则新集合表示的区间会产生“断裂”,即 x 依旧无法被表示,所以该集合的神秘数还是x
。基于以上分析,产生下面的算法,用以求一个给定集合的神秘数:
首先设ans = 1
,作为最初的神秘数,然后求出 ∑ai≤ansai
那么如果get < ans
,则ans
就是神秘数,否则令ans = get + 1
,继续过程。【CC DGCD】Dynamic Gcd
题意:给定一棵
n
个结点的带点权的树,有Q
次操作:
(1)询问两点l
、r
之间路径上所有结点点权值的最大公约数。
(2)给两点l
、r
之间路径上所有点的权值加上一个值val
。
1≤n,Q≤50000TAT (转化
利用树链剖分 +dfs
序,可以将询问转化为求 O(logn) 个区间的 gcd,修改也转化为对 O(logn) 个区间进行区间加
根据 辗转相减法,gcd(a[1], a[2] ... a[k]) = gcd(a[l], a[2] − a[1], ... a[k] − a[k − 1])
定义一个差分数组c[i] = a[i] − a[i − 1]
那么gcd(a[1], a[2] ... a[k]) = gcd(a[1], c[2] ... c[k])
对于区间l ~ r
加操作,只要让c[l]
加上val
、c[r + 1]
减去val
。对于a[1]
和c[k]
特判一下即可。
线段树维护下区间gcd
即可。
时间复杂度: O((n+Q)log3n)方程最小值
题意:给定
n
对整数k[i], b[i]
令 fm(x)=∑mi=1|ki∗x+bi|
求 fi(x) 的最小值,对每个i
从1 ~ n
求一次值(x
是未给定的
1≤n≤105,0≤|k|≤103,0≤|bi|≤109TAT (做法
如果k = 1
,易得应该让x
取的是−b[i]
的中位数(可以自己举个例子
如果k < 0
,则让k
,b
变成−k
,−b
也不影响答案,所以可以假设k > 0
于是|kx + b| = k|x + b / k|
,相当于有k
个|x + b / k|
用线段树维护一下中位数即可【BZOJ 4456】【Zjoi 2016】旅行者 题解
不妨设
n <= m
那么两点间的最短路要么跨过中线,要么两点在中线的同一侧
对于跨过中线的情况,我们可以对中线上的每一个点求出他到其他点的最短路,然后枚举这条路径是跨过中线的哪一个点
对于在中线的同一侧的情况,我们递归求解
(不会证复杂度,证明