O
NOIP1
[ABC098D] Xor Sum 2
异或相当于不进位加法,所以如果一段区间中某一位有多于一个的 \(1\) 就不行,考虑用 \(20\) 个指针分别维护每一位上一个 \(1\) 的位置,没出现记为 \(-1\),那么扫右端点时更新一下就可以了,时间复杂度 \(\mathcal{O}(n \log V)\)。
但是完全没必要这么麻烦,因为我们找的是只出现一个 \(1\) 的区间,很明显这是有单调性的连续的一段,使用前缀和和异或和直接双指针 check 即可,时间复杂度线性。
P3800 Power收集
容易列出 dp 式子:\(f_{i,j}=\max_{j-L \le k \le j+L} f_{i-1,k}+a_{i,j}\)。转移是区间形式,使用单调队列优化即可。
P2680 [NOIP2015 提高组] 运输计划
最大值最小,考虑二分。对于 \(\le mid\) 的不用管。记下 \(>mid\) 的路径。枚举边,看看被几条 \(>mid\) 的路径覆盖了,这个用树上差分实现,如果被所有的路径覆盖且时间的最大值减去这个边权 \(\le mid\) 就合法。
P2034 选择数字**
最多 \(k\) 个这个限制在 dp 的过程中你会发现并不好维护。那么可以考虑正难则反,转化为删除若干个数,且 \(k+1\) 个数中至少要删一个,求最小代价。
直接设 \(f_i\) 表示删第 \(i\) 个数的最小代价即可。\(f_i=\min_{i-k-1\le j <i} f_j+a_i\),单调队列优化。
极限情况就是 xoooox 这样,中间长度是 \(k\),所以左边是 \(i-k-1\)。最后的答案要在 \([n-k+1-1=n-k,n]\) 里面找。
但是直接做也不是不行,不好维护是因为你不确定这个数的信息,所以改设 \(f_{i,0/1}\) 表示 \(i\) 选没选就行了。
P1419 寻找段落
二分答案,然后将 \(a_i\)减去 \(mid\),转化为你需要检查是否存在一个和非负的区间。这一步可以从分数规划的角度考虑,一个数对分子的贡献是 \(a_i\),对分母的贡献是 \(1\)。
求出前缀和 \(S\)。枚举右端点 \(i\),你要求出 \(S_i-S_{l-1}\) 的最大值即 \(S_{l-1}\) 的最小值,且 \(l-1\) 要合法。因为 \(s \le i-l+1 \le t\),所以 \(i-t \le l-1 \le i-s\),单调队列。
P2671 [NOIP2015 普及组] 求和
\(y\) 没啥用,看 \((x,z)\),只要 \(x+z\) 为偶数即可。拆开贡献,就是 \(xa_x+za_x+xa_z+za_z\)。按颜色枚举 \(z\),考虑它前面的 \(x\) 需要与 \(z\) 奇偶性相同,然后这样的 \(x\) 贡献分成上面四类。
第一类,\(xa_x\),直接累加和即可;第二类,\(za_x\),\(z\) 是固定的,累计 \(a_x\) 的和即可;第三类同理;第四类,\(za_z\),要计算的次数是前面 \(x\) 的个数。在线性时间内解决了本题。
P1311 [NOIP2011 提高组] 选择客栈*
算法一:枚举颜色和选取的右端客栈,那么左端客栈必须在当前点前第一个可以作咖啡厅的点和它的左边,同时也必须在当前点左边,开一个当前颜色的桶即可。时间复杂度 \(\mathcal{O}(nk)\)。
算法二:先算出所有同色的方案数,就是对每种颜色开个桶。然后不合法的方案中间必然没有咖啡厅,那么合法的咖啡厅把序列分成了若干段,每个不合法的对必须在某两个相邻的咖啡厅之间,同样开桶计算即可,这是线性的。
算法三:考虑直接枚举右端点,同时开着所有颜色的桶,并记录每种颜色可用的数量。记下目前最右的咖啡厅位置。然后,每扫到一个颜色,如果它之前不可用,但是上次出现在咖啡厅之前,那么可用的数量直接赋成桶的大小即可。这主要运用了延迟更新的思想,反正用到的信息是对的就行了,也是线性的。
P1950 长方形*
首先可以设 \(nxt_{i,j}\) 表示 \((i,j)\) 这个位置后面第一个涂过的格子的位置,没有就设为 \(m+1\),然后枚举左上角,一行行向下扩展,此时可以取的右端点就是经过的 \(pos_{,j}\) 的最小值,这样是一个小常数 \(\mathcal{O}(n^2m)\) 做法。
求这个式子的值 \(\sum_{i=1}^n \sum_{j=1}^m\sum_{k=i}^n \min_{i \le w \le k}p_{w,j}\)。因为减 \(j\) 是好计算的。
固定 \(j\),问题转化为 \(\sum_{i=1}^n \sum_{j=i}^n \min_{i\le k \le j} p_k\),也就是所有子区间的最小值之和。单调栈。复杂度 \(\mathcal{O}(nm)\)。
悬线法还没看懂。
P3406 海底高铁
差分维护每条边被经过的次数,然后比较一下即可。可以用 \(i\) 处的值表示边 \((i,i+1)\)。
P3143 [USACO16OPEN] Diamond Collector S
排序后考虑枚举第二个书架的终点 \(i\),那么起点就是第一个 \(\ge a_i-k\) 的数,另一个书架必须在它前面,再记一个答案的前缀 max 即可。感觉这个超级简便啊。
Bonus:如果是三个书架呢?
P4653 [CEOI2017] Sure Bet
二分,然后考虑 check。枚举选取灯的数量 \(i\),那么两种灯的权值和都要不小于 \(mid+i\),那肯定是先取大的,用两个指针维护一下,看最后有没有超过 \(i\) 的限制即可。
P2216 [HAOI2007] 理想的正方形
二维单调队列?我不会啊。无所谓,先对行做单调队列求出每个位置的最大值然后再对列做单调队列即可。
P1638 逛画展
有单调性,双指针维护一下即可。删到只剩 \(m-1\) 个数,如果这个只剩 \(m-1\) 个数是删出来的就是一个合法的区间。
NOIP3
CF609E.Minimum spanning tree for each edge
首先考虑随便求出一个最小生成树,然后考虑一条边,如果在最小生成树上了显然不用管,否则先加入它,在原树上形成了一个环,接下来想要的是最小生成树,所以把这个环上边权最大的边删去但不能删自己,因此就是 MST 上权值最大的边。倍增即可。
NOIP4
P5677 [GZOI2017] 配对统计
因为数值互不相同,所以前驱后继最多各有一个。如果前驱后继能取最小就记下它的位置,查询就变成了二维数点,树状数组一个 \(\log\)。
P6327 区间加区间 sin 和
考虑线段树的 pushtag,我们知道 \(\sin(\alpha+\beta)=\sin(\alpha)\cos(\beta)+\sin(\beta)\cos(\alpha)\),所以对于加 \(v\) 的操作,只需要维护区间 \(\sin\) 和和区间 \(\cos\) 和就可以轻松维护。注意下传信息时不要用更新过的信息,即要先存一下。一个 \(\log\)。
P7706「Wdsr-2.7」文文的摄影布置
如果你想到线段树,这题大概就会了。遇到区间问题,还是要多多尝试线段树啊。
考虑线段树维护区间答案,那么 \(i,j,k\) 可能是都在左边或右边,这个直接更新就好了。如果 \(i\) 在左边,\(j,k\) 在右边,那么左边只需取一个最大的 \(a_i\),右边要取最大的 \(a_k-b_j(k>j)\)。如果 \(i,j\) 在左边,就要取最大的 \(a_i-b_j(i<j)\)。
现在只需分别维护 \(i>j\) 和 \(i<j\) 的 \(\max a_i-b_j\) 即可,更新同样考虑 \(i,j\) 在一侧和 \(i,j\) 在两边,最后你还需要维护 \(a\) 的最大值和 \(b\) 的最小值。如果无法取到,比如说在叶子不可能有 \(i<j\),那么答案要设为 \(-\infty\)。时间复杂度一个 \(\log\)。
P5482 [JLOI2011] 不等式组
就,解呗。\(ax>c-b\),若 \(a>0\) 则所有满足 \(x >\frac{c-b}{a}\) 的 \(x\) 都行,因为 \(x \in \mathbb{Z}\),所以 \(x \ge \lfloor \frac{c-b}{a} \rfloor +1\),贡献到一段区间。\(a<0\) 同理。还有 \(a=0\) 要单独处理。区间修改单点查询,值域很大,直接用动态开点线段树,差分转成单点查区间加即可。复杂度 \(\mathcal{O}(n \log V)\)。
P3792 由乃与大母神原型和偶像崇拜
本题有两个经典且重要的结论。
1.如果出现的数不重复,那么区间为值域连续段当且仅当区间内的 \(\max-\min=r-l\)。
2.为判断一个区间内是否有重复的数,可以开一个数组 \(pre\) 表示第 \(i\) 个数上一次出现的位置,查询就是 \([l,r]\) 中的 \(pre\) 最大值 \(<l\) 即可。
为了快速维护 \(pre\),可以用好多个 set 维护每个数出现的所有位置,然后每次会影响的位置是它自己,原来这个数的后继,修改后这个数的后继,算一下即可。
P5278 算术天才⑨与等差数列
首先判掉 \(k=0\),此时只需最大值与最小值相等。
否则,首先区间内不能有重复数字,然后 \(\max-\min\) 应为 \((r-l)k\),然后相邻两数差的 \(\gcd\) 应该为 \(k\),必要性都是显然的,为什么是充分的呢?
考虑排序后减去最小值,序列就变为了 \([0,a,b,\dots,(r-l)k]\),此时等差数列应为 \([0,k,2k,\dots,(r-l)k]\)。然后相邻数差的 \(\gcd\) 为 \(k\) 说明原来任意两数的差都是 \(k\) 的倍数,现在减掉了最小值那么 \(a,b\) 什么的都是 \(k\) 的倍数。除掉,序列变为 \([0,a/k,b/k,\dots,r-l]\),等差数列为 \([0,1,2,\dots,r-l]\)。因为没有重复数字,所以原序列也只能是 \([0,1,2,\dots,r-l]\),得证。
从上一题的代码上加个 \(\gcd\) 即可。
P6617 查找 Search*
有了前两题的经验,容易想到记 \(pre_i\) 表示 \(i\) 前满足 \(a_i+a_j=w\) 的最大的 \(j\),查询是区间内的 \(pre\) 最大值 \(\ge l\) 即可。
前两题直接存前驱,是因为前驱的修改只会影响到后面的第一个数。但是这里不同,这里一个数的修改可能会影响到后面大量的数。因为可能后面有很多数都是与这个数相加为 \(w\)。
考虑修改 \(pre\) 的定义让这里的修改也只会影响到一个数,那么应该是只有后面的第一个满足与当前数和为 \(w\) 的位置被修改。那么也就是说,只有后面第一个满足与当前数和为 \(w\) 的位置的 \(pre\) 值为当前数。因此,如果 \(i\) 前第一个与 \(a_i\) 和为 \(w\) 的位置在 \(a_i\) 上一次出现的位置之前,那么 \(pre_i=0\)。显然这样不会影响正确性,因为如果 \((i,j)\) 为 \(|j-i|\) 最小的答案,那么 \(pre_j=i\)。我们一定可以找到这组答案。
那么这样做修改除了会影响到前面的那三个位置,还会影响到等于 \(a_i\) 的后继,因为它们可能成为了第一个位置,总共五个数。
P2471 [SCOI2007] 降雨量
记下每个年份有没有出现过,每次询问找到对应的输入年份区间,用 st 表维护最大值。分类讨论一下两个端点即可。
P6105 [Ynoi2010] y-fast trie
首先 \((i+j) \bmod C=(i \bmod C+j \bmod C) \bmod C\),因此可以先取模,这样整个序列中都是 \([0,c)\) 间的数。那么对于一对 \((i,j)\),要么 $ 0 \le i+j<C$,要么 \(C \le i+j <2C\)。
对于第二种,答案为 \(i+j-C\),我们只要让 \(i+j\) 尽量大就行了,这部分的答案是最大值与次大值之和减去 \(C\)。当然 \(i+j\) 不一定比 \(C\) 大,会减出负数,但这样的话最优答案就和这种情况没有关系,所以直接算即可。
对于第一种,我们应该对每个数 \(x\) 找到一个最大的 \(y\),使得 \(x+y\le C-1\),即 \(y \le C-x-1\) 的最大值。此处有一个细节,如果 \(x\) 只出现了一次,它对应的 \(y\) 不能选自己。
注意,如果 \(x\) 选了 \(y\),\(y\) 也不一定会选 \(x\)。虽然 \(y\) 已经是 \(x\) 最好的选择了,但是 \(y\) 可能还有更好的选择。因此匹配是单向的。
这样修改一个数可能会影响到 \(\mathcal{O}(n)\) 组匹配,不可行。考虑数对问题,看看是不是有大部分数对是没用的。如果 \(i\) 选了 \(j\) ,而 \(j\) 选了 \(k\),那么 \(i\) 肯定对于 \(j\) 没有 \(k\) 好,所以 \(i+j\) 也没有 \(j+k\) 大,所以 \((i,j)\) 这一对就完全没用了。把这些选择去掉后,如果 \((x,y)\) 被选了,那么 \(x\) 一定选 \(y\),\(y\) 也一定选 \(x\),所以我们只需要维护这样的双向选择。
考虑插入了一个 \(x\),\(x\) 的最优匹配是 \(y\)。如果 \(y\) 没有双向匹配,那么直接在 \(x,y\) 间建立双向匹配即可。如果 \(y\) 之前已经有了这种双向的匹配关系,假设 \(y\) 匹配的是 \(x'\),如果 \(x'+y<x+y\),那么新的匹配更优,断开 \(y\) 和 \(x'\) 的匹配,并将 \(x,y\) 建立匹配,这样的话 \(x'\) 找到的最优匹配仍然是 \(y\),因此没有要变化的的了。如果原先的匹配更优就不操作。
考虑删除了一个 \(x\),如果 \(x\) 还剩大于一个,什么都不用管;如果此时恰好剩下一个 \(x\) 且 \(x\) 的最优匹配就是 \(x\),那么要断开这个匹配并为 \(x\) 重新寻找匹配。如果 \(x\) 没有了,如果 \(x\) 有匹配,就断开这个匹配,同时对它的匹配点寻找新匹配,这里的匹配必须也是双向的。
开两个 multiset 分别维护集合中的数用于寻找匹配和可能的答案即可。