NOIP Practice Recordings DS

Data Structure

segment tree

luogu1438 无聊的数列

  (2022.4.1)

  就是区间加等差数列和单点询问。

  直接线段树维护差分数组,每次对于一个修改 [ l , r ] [l, r] [l,r],在 l l l 处加上 s s s(等差数列首相),在 [ l + 1 , r ] [l + 1, r] [l+1,r] 处加上 d d d (等差数列公差),在 r + 1 r + 1 r+1 处减去 e e e(等差数列末项)。然后每一次询问 p p p求出 ∑ i = 1 p c [ i ] \sum\limits_{i = 1}^pc[i] i=1pc[i] 就好了。

P4587 [FJOI2016]神秘数

  (2022.5.21)

  求区间最小的不能被子集合表示出的数。

  考虑一个区间 [ l , r ] [l, r] [l,r],对这一段进行一个升序排序,设排序之后的数分别为 a l ≤ a l + 1 ≤ ⋯ ≤ a r a_l \leq a_{l+1} \leq \cdots \leq a_r alal+1ar,现在对于 a l a_l al 如果不等于 1 1 1,那就可以直接输出 1 1 1。若等于 1 1 1 我们考虑一个简单的 d p dp dp

  现在用 p o s pos pos 表示当前能表示出的数属于的区间为 [ 1 , p o s ] [1, pos] [1,pos](很显然这个区间包括了 1 1 1),对于下一个数 a i a_i ai,现在有两种情况:

  1. 如果 a i ≤ p o s + 1 a_i \leq pos + 1 aipos+1,那么现在能表示出的数的集合就是 [ 1 , p o s ] ⋃ [ 1 + a i , p o s + a i ] = [ 1 , p o s + a i ] [1, pos] \bigcup [1 + a_i, pos + a_i] = [1, pos + a_i] [1,pos][1+ai,pos+ai]=[1,pos+ai]
  2. 如果 a i > p o s + 1 a_i> pos + 1 ai>pos+1,那么现在能表示出的数的集合就是 [ 1 , p o s ] ⋃ [ 1 + a i , p o s + a i ] [1, pos] \bigcup [1 + a_i, pos + a_i] [1,pos][1+ai,pos+ai],显然这个集合中不包含 p o s + 1 pos + 1 pos+1,所以答案就是 p o s + 1 pos + 1 pos+1

  这样做对于每次询问的时间复杂度是 O ( n log ⁡ n ) O(n\log n) O(nlogn) 的,所以要考虑优化这个做法。

  如果现在的值域是 [ 1 , p o s ] [1, pos] [1,pos],那么现在的答案就应该是 p o s + 1 pos + 1 pos+1。记小于等于 a n s ans ans 的数的和为 r e s res res,如果 r e s ≥ a n s res \geq ans resans,那么就说明一定有没有选过(选一些数的和为 a n s ans ans)的且小于等于 a n s ans ans 的数存在,那么现在令 a n s = r e s + 1 ans = res + 1 ans=res+1,反过来就说明答案就是 a n s ans ans,直接 b r e a k break break 就好了,注意到 ∑ a i < 1 e 9 \sum a_i < 1e9 ai<1e9,所以可以考虑用主席树维护区间值域和。

BIT

luogu8251 丹钓战

  (2022.3.26) / 这个题也可以用主席树过掉qwq

  很多二元组 ( a i , b i ) (a_i, b_i) (ai,bi),按照 a i a_i ai 不同和 b i b_i bi 递增来维护单调栈,然后每次询问给定一个区间 [ l , r ] [l, r] [l,r] 询问在这个区间里维护单调栈的时候栈中只有一个元素的时刻的个数。

  预处理出每个位置 i i i 入栈前弹栈后的栈顶,记为 p [ i ] p[i] p[i]。然后问题就能转化为 [ l , r ] [l, r] [l,r] 区间中有多少 p [ i ] p[i] p[i] 小于 l l l。因为如果 p [ i ] p[i] p[i] 小于 l l l 就说明在扫到 i i i 的时候就已经把 l l l 之后的二元组全部弹出栈了(要不然也不会是栈顶了嘛)。然后就可以把所有询问离线下来然后对于左端点排序,处理出扫到一个左端点的时候左端点的值。然后再关于右端点排序,处理出扫到右端点的时候小于左端点的值,处理可以用树状数组做。

luogu1966 火柴排队

  (2022.4.16)

  给两个序列 { a n } \{a_n\} {an} { b n } \{b_n\} {bn},然后定义这两个序列的距离为 ∑ i = 1 n ( a i − b i ) 2 \sum\limits_{i=1}^n(a_i - b_i)^2 i=1n(aibi)2。现在给你一个操作,每次操作交换一个序列中相邻的两个数,问最少操作多少次使得两数组的距离最小。

  我们考虑展开这个式子:

∑ i = 1 n ( a i − b i ) 2 = ∑ i = 1 n ( a i 2 + b i 2 − 2 a i b i ) = ∑ i = 1 n a i 2 + ∑ i = 1 n b i 2 − 2 ∑ i = 1 n a i b i \begin{aligned} & \sum_{i = 1}^n (a_i - b_i)^2 \\ = & \sum_{i = 1}^n (a_i^2 +b_i^2 - 2a_ib_i) \\ = & \sum_{i = 1}^n a_i^2 + \sum_{i = 1}^n b_i^2 - 2\sum_{i = 1}^n a_ib_i \end{aligned} ==i=1n(aibi)2i=1n(ai2+bi22aibi)i=1nai2+i=1nbi22i=1naibi

  我们发现 ∑ i = 1 n a i 2 + ∑ i = 1 n b i 2 \sum\limits_{i = 1}^n a_i^2 + \sum\limits_{i = 1}^n b_i^2 i=1nai2+i=1nbi2 是不受数列顺序影响的,所以我们有:

( ∑ i = 1 n ( a i − b i ) 2 ) m i n = ∑ i = 1 n a i 2 + ∑ i = 1 n b i 2 − 2 ( ∑ i = 1 n a i b i ) m a x = C o n s t A + C o n s t B + 2 ( ∑ i = 1 n a i b i ) m a x \begin{aligned} \left(\sum_{i = 1}^n (a_i - b_i)^2\right)_{min} = & \sum_{i = 1}^n a_i^2 + \sum_{i = 1}^n b_i^2 - 2\left(\sum_{i = 1}^n a_ib_i\right)_{max} \\ = & Const_A + Const_B + 2\left(\sum_{i = 1}^n a_ib_i\right)_{max} \end{aligned} (i=1n(aibi)2)min==i=1nai2+i=1nbi22(i=1naibi)maxConstA+ConstB+2(i=1naibi)max

  所以现在就考虑如何最大化 2 ∑ i = 1 n a i b i 2\sum_{i = 1}\limits^n a_ib_i 2i=1naibi

  这里就有一个结论,就是说如果 a i a_i ai b i b_i bi 都是升序的时候上面的式子就取到最大值。现在考虑如何证明,考虑微扰法。

  假设 a a a b b b 已经排好序了,现在答案就是 ∑ i = 1 n a i b i \sum\limits_{i = 1}^n a_ib_i i=1naibi,我们考虑交换 a i a_i ai a i + 1 a_{i +1} ai+1 a i < a i + 1 a_i < a_{i +1} ai<ai+1)。那么现在的答案就是 ∑ i = 1 n a i b i + ( a i b i + 1 + a i + 1 b i ) − ( a i b i + a i + 1 b i + 1 ) \sum\limits_{i = 1}^n a_ib_i + (a_ib_{i+1} + a_{i+1}b_i) - (a_ib_i + a_{i+1}b_{i+1}) i=1naibi+(aibi+1+ai+1bi)(aibi+ai+1bi+1),然后对式子进行化简:

( a i b i + 1 + a i + 1 b i ) − ( a i b i + a i + 1 b i + 1 ) = a i b i + 1 − a i b i + a i + 1 b i − a i + 1 b i + 1 = ( a i − a i + 1 ) ( b i + 1 − b i ) < 0 \begin{aligned} & (a_ib_{i+1} + a_{i+1}b_i) - (a_ib_i + a_{i+1}b_{i+1}) \\ = & a_ib_{i+1} - a_ib_i + a_{i +1}b_i - a_{i +1}b_{i +1} = (a_i - a_{i+1})(b_{i +1} - b_i) < 0 \end{aligned} =(aibi+1+ai+1bi)(aibi+ai+1bi+1)aibi+1aibi+ai+1biai+1bi+1=(aiai+1)(bi+1bi)<0

  所以我们发现,如果对这个数列进行微扰就会使答案减小,所以数列在升序的时候答案最大,证毕。

  这样之后,我们就是要把两个数列交换成升序的为止互相对应就好了(注意,不是真的升序而是升序的位置对应),那就先考虑将两个数组离散化然后构造一个新的数组 q i q_i qi,其中 q a i = b i q_{a_i} = b_i qai=bi。这样的话如果位置是对应的就应该有 q i = i q_i = i qi=i 也就是 q i q_i qi 升序,那么我们只需要求出 q q q 的逆序对个数就是这道题要求的答案了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值