杭州集训 Day 2
课前
由于昨晚打了 ABC 很坐牢所以多睡了一会 6:30 才起,酒店的饭又贵又难吃于是我们选择点外卖,但是早上的外卖都是 \(20\) 元起送,很麻烦,所以和 htdlz 拼了一单。花十块钱买了粥,没吃完,最后吃的 hanss6 的榨菜才咽下去。
今天 hs_black 没有迟到,但是讲的题很抽象,六个小时讲二十多个数据结构题着实吓人。
笔记
P4168
预处理 \(a[i][j]\) 表示第 \(i\) 块到第 \(j\) 块的众数,预处理 \(cnt[i][j]\)
表示第 \(i\) 种蒲公英在前 \(i\) 块的出现次数。记录零散块里面每个颜色出现了多少次,并加上其在中间一部分的出现次数作为潜在众数更新答案。
P3203
分块,维护 \(f_i\) 表示到达 \(i\) 号点后第一次弹出其所在块会弹到哪个点,并且记录弹跳次数。这样每次都能跳出一整块,最多跳 \(\sqrt n\) 次。维护 \(f_i\) 可以每次修改直接重构整个块
P2391
倒序操作,需要支持将区间没有染色的地方染色。
并查集,暴力染色,并删除染过的地方。令 \(f[i]\) 从 \(i\) 开始第一个没有染色的地方。染色后将 \(f[i]\) 设置为 \(f[i]+1\),利用并查集维护。
CF571D
如果得到某个位置最后一次清 \(0\) 是什么时刻,那么只考虑这个时刻之后的加法操作即可。
我们用启发式合并维护信息,第二类集合维护上一次清零的时间,清零就在根结点处打 \(tag\),需要记录连接的时间。
然后可以离线下来,变成前缀和相减。
加法操作可以记录自己连上来时候父亲的 \(tag\),查询时拿父亲的 $ tag$ 减去记录的值即为正确的答案。
P4755
对于当前区间 \([l,r]\) ,先判断区间长度是否为 \(1\),为 \(1\) 只需要特判当前这个数是不是 \(1\) 即可。
否则找区间最大值的位置 \(mid\), \(ST\) 表维护。之后统计过 \(mid\) 的所有 \([l,r]\),递归计算 \([l,mid-1],[mid+1,r]\) 即可。
枚举 \(mid\) 的一边。假设枚举的数是 \(a_i\),需要在 \(mid\) 的另一边找到一个 \(a_j\),满足 \(a_i*a_j\leqslant a_{mid}\),即 \(a_j\leqslant \lfloor\frac{a_{mid}}{a_i}\rfloor\)。
现在问题变为统计一个区间内小于某个数的个数,可持久化线段树维护。
枚举时用启发式的思想,只枚举区间长度小的那边,就可以做到\(O(nlog^2n)\)。\(a_i\) 很大,要离散化。
P1972
离线下来,按照右端点从小到大排序。为了保证每个颜色只贡献一次,让其在最右边的点贡献,其他的点设为 0 贡献。直接求后缀和。
CF1000F
设 \(pre_i\) 表示上次出现的时间,如果存在 \(pre_i<l\) 说明有只出现一次的数,维护 \(pre_i\) 的最小值
CF522D
将询问离线下来,将 \(r\) 从小到大排序,每个点记录和下一次出现的距离,如果下一次超过 \(r\) 距离为无限,查询区间最小值即可。
P4247
听懂了但也仅限于听懂了,不理解这么抽象的题就评个蓝
线段树上每个节点维护一个 \(f[j]\) 表示选出 \(j\) 个数相乘的和,那么合并两个节点就是 \(root.f[a+b]=∏left.f[a]\times right.f[b]\)
区间加,选 \(c\) 个数相乘,\(f[c]=∏a_i\),\(f'[c]=∏(a_i+t)=\sum t^i f[c-i] C_{len-c+i}^{i}\)
相反数,那么选奇数个取反,偶数个不变。
P5579
按照 \(a_i\) 排序,发现序列每时每刻都是一个单增的序列,那么每次割草都是一个后缀。如果找到这个后缀就可以轻松割草,可以线段树上二分 + 区间赋值。线段树维护最大值,赋值标记,赋值时间,区间和。
SP2713
经过几次开方,答案就会变成 1,考虑暴力做直到一个区间里都是 1 为止。
P3722
对于一个区间,在其内部最大值上计算答案。
设 \(L[i]\) 表示左边第一个大于自己的下标, \(R[i]\) 表示右边第一个大于自己的下标。那么 \(L[i]\)~\(R[i]\) 的代价是 \(p1\),\(L[i]+1\)~\(R[i]\) 的代价是 \(p2\),\(L[i]\)~\(R[i-1]\) 的代价是 \(p2\)。统计价值:二维数点
CF526F
统计满足 \(max-min+1=r-l+1\) 的区间的个数。从左边到右边枚举 \(r\) ,我们发现一定有 \(max-min-(r-l)≥0\),也就是 \(max-min+l≥r\),线段树维护,如果最小值为 \(r\) ,答案就是 \(r\) 的个数,由于扫描的序列位置单调可以考虑使用单调栈维护更新扫描线,弹栈的话就对应区间加减。
P4378
对于每个数 \(x\),每次都会恰好把 \(>x\) 的一个数字交换到后面,如果没有说明序列排好序了。因此答案为 \(max(1,(\sum _{j<i}[a_j>a_i]))\)
P4375
省流:把题目给的代码改成 C++ 有 50pts,卡常后有 60pts
首先对于原数组进行离散化。第一次:找出一个最大值放到最后面。对于位置 \(x\),冒泡之后一定有一个大于 \(x\) 的数被放到了 \(x\) 后面。
第二次:找出一个最小值放到最前面。同理,对于位置 \(x\),一定有一个小于等于 \(x\) 的值放到了 \(x\) 的前面。
结论就是:\(ans = max(ans, x 前 > x 的数的个数)\)
P4372
称一个序列是有序的当且仅当所有点都是分割点,对于每个点它不用在进行冒泡排序了当且仅当两边都已成为分割点,也就是两边出现时间的最大值,可以求出每个点被排了几次。离散化,然后对于一个点 \(x\) 来说, 所有小于它的数却在它后面的, 每一次都会向前走一次,那么它出现的时间就是离它最远的小于它的点冒泡到它前面的时间,即那个点到它的距离,
所以单调队列或指针都可以维护。
P6225
如果 \(l,u\) 奇偶性不同,每个点贡献偶数次,答案是 \(0\)。
否则和 \(l,u\) 奇偶性相同的位置贡献一次,其他点贡献为 \(0\),所以我们用两个树状数组分别保存奇数位置的异或和和偶数位置的前缀异或和。
P3760
设前缀和为 \(s_i\),考虑二进制第 \(k\) 位,计算有多少对 \(s_j-s_i\) 的第 \(k\) 位为 \(1\),如果有奇数对,那么最终答案这一位就是 \(1\),否则这一位就是 \(0\)
枚举 \(s_j\) 如果 \(s_i=j\) 第 \(k\) 位是 \(1\):如果 \(s_i\) 第 \(k\) 位是 \(1\),那么产生借位才会让第 \(k\) 位是 \(1\),所以应该满足 \(s_j\) 第
\(k\) 位之后的部分 \(<s_i\) 第 \(k\) 位至后的部分。如果 \(s_i\) 第 \(k\) 位是 \(1\),那么不产生借位才会让第 \(k\) 位是 \(1\),所以应该满足 \(s_j\) 第 \(k\) 位之后的部分 \(≥s_i\) 第 \(k\) 位至后的部分。\(s_j\) 第 \(k\) 位是零同理。
条件为偏序关系,可以使用树状数组维护,开两个树状数组分别维护第 \(k\) 位为 \(1\) 或 \(0\) 的后 \(k-1\) 位部分即可。
CF718C
对于斐波那契数列显然可以矩阵加速递推,但是发现修改操作并不能直接线段树维护,因为其操作对象一个是 \(a\) 一个是 \(f\) ,所以考虑想办法统一操作对象这样就可以使用一颗线段树进行维护了。对于 \(a\) 的修改操作,可以直接等价理解为给 \(f\) 乘上 \(1110\) 那个矩阵的 \(x\) 次方,这样就可以用一颗线段树进行维护了。
P7453
直接维护三颗线段树并不现实,考虑维护操作矩阵。那么这七种区间修改可以认为是乘上一个矩阵,所以我们维护一个区间乘法懒标记,线段树即可(卡常)
P5537
考虑到每个点从根节点到其的路径是唯一的,设为 \(p_x\),可以将操作 \(1\) 转化为从根节点开始,在操作区间前加入一段序列。考虑中间经过的某个节点 \(x\),那么 \(p_x\) 一定为操作区间的一段前缀,二分 + 哈希即可。现在加上了修改,那么可以有 \(O(n \log^2 n)\) 的树状数组维护哈希值,也可以做线段树上二分变成单 \(\log\)
课后
决定加入绝区零了,崩铁能肝的都肝完了,静等新版本了。(不可能玩原的)