【题解】Solution Set - NOIP2024集训Day1 数据结构
【题解】Solution Set - NOIP2024集训Day1 数据结构
https://www.becoder.com.cn/contest/5429
「CF1428F」Fruit Sequences
线段树是可以维护区间最长子段的 1。
记固定右端点在 \(i\),的答案为 \(f_i\)。那么:
- \(a_i=0\),\(f_i=f_{i-1}\);
- \(a_i=1\),打一个单调栈维护所有的最长子段,然后来更新 \(f_i\)。
p.s. 代码里面的定义有些不一样。
「2021牛客暑期多校训练营2 - G」League of Legends
有一道几乎一样的题 NOIP模拟赛1 T3 色;题解
不一样的是这道题不允许有空集,也就是说一开始存在空集的情况就排除掉了。
然后在 dp 的时候,不能直接前缀和,还需要一个单调队列,来判一下合不合法。
p.s. 很好,下面是自己的思路,可以发现几乎全部错了 QAQ,所以就别看了。(唯一值得高兴的是看了之前的题解之后,一次性改对了
问题主要如下:
- 一开始的结论就假了,没有考虑完全包含的情况。以后还是需要再多想一下;
- \(calc(i,j)\) 因为必须有交集,实际上就等于 \(r_i-l_j\),而没有想到这个这个的话断送了后面优化的路。
下面是原文:
有难度。👍
先思考一些性质。
首先,对所有区间排序。
然后,可以发现划分出来的 \(k\) 个组每个组都一定是连续的(可以反证),这样就很好 dp 了。
定义 \(f_{i,j}\) 表示前 \(i\) 个中分为 \(j\) 组的最优解,\(calc(i,j)\) 是第 \(i\) 个区间到第 \(j\) 个区间交的长度。有转移:
然后发现对于相同的 \(i\),\(calc(i,j)\) 随着 \(j\) 的增大而减小,而且越靠前的 \(i\) 减小的越快。
所以我们可以维护一个 “类单调队列”。
末端加入元素的时候,需要保证比前面的优。
然后队首删除那些
所以有决策单调性,预处理出 \(calc\),然后就可以 \(O(n^2)\) 做了。
「ARC067F」Yakiniku Restaurants
首先走回头路肯定是更劣的,所以最终答案一定是走的一个区间。
\(a\) 求前缀。
考虑一个区间 \([l,r]\) 的答案应该是:
想一想用 ST 表可以做到 \(O(n^2m)\),但是还是过不了。
\(m\) 很难搞掉,所以考虑搞掉一个 \(n\)。
自然想到 two-points。
正确性?
对于一个右端点 \(r\),设她的最优左端点为 \(l\),记作 \([l,r]\)。对于 \([l,r+1]\),设左端点变为 \(l-1\) 之后,\(B\) 求和的增量为 \(\Delta\),那么对于 \([l,r]\),该值一定是 \(\le \Delta\) 的,而要减去的 \(l-1,l\) 之间的距离是不变的。也就是说左端点左移的贡献在减少!故而左端点一定不会左移。
好吧,事实证明,正确性的问题有点大😥
two-points 是错的,因为左端点向右移动的时候,可能先是不优的,然后再是最优,所以就 G 了。
https://www.becoder.com.cn/submission/2566864
https://www.luogu.com.cn/article/9rwufdjc
事实上,还是得搞掉一个 \(m\)。我们不妨把 \(B\) 对每个区间的贡献提前加上。
先预处理当前 \(B_{i,j}\) 作为最大值的极长子段。设为 \([L,R]\)。
然后对于左端点在 \([L,i]\),右端点在 \([i,R]\) 的区间,一定是 \(B_{i,j}\) 做贡献。用二维差分实现二维矩阵加法。
然后再 \(O(n^2)\) 枚举左右端点就行了。总时间复杂度 \(O(nm+n^2)\)。
做完上面两道题,感觉小脑萎缩了😭
每次想的都是错的,而且感觉有点过度深挖性质了。
实际上直接面对这个式子,直接处理可能就行了。
「ARC072F」Dam
dp 的话,\(O(nt^2)\),难以优化。
考虑到,最后的形式一定是:
所以就是要最大化分子。
题解(可以稍微看一下 \(f\) 的定义,然后我自己按照自己感觉更自然的思路来理解了一下。
我们考虑把整个过程抽象成函数上。以此来挖掘一些性质。
具体在这道题上,就是题解中所定义的 \(f\)。
将每一天的操作分为如下两步:
-
加水: 先不考虑容量限制。整个函数就相当于在最开始加一条线段 \((0,0)-(v_i,v_it_i)\),然后再在另一个端点接上原来的函数。
因为每次我们都是加入了一个单增的东西,而后面的东西增减性不变,所以整体就一定是单增的。
-
倒水: 对于任意容量为 \(x\) 的状态开始倒水,其实就是从这个点向原点连一条线段,表示上面的点都可以得到。
又因为要取 \(\max\),并且函数单增,所以整个函数应该上凸的。
然后就要用单调队列维护这个上凸包。
首先第一步要求我们整体平移,所以我们直接维护每一个拐点和前一个的差值。
第二步要求我们找到斜率最大的一个拐点,然后用这条线段替换掉前面的所有线段。