赛时4题,打得很差的一场。尤其是E题,赛时一直被卡,直到结束看群友的结论后顿然醒悟,不到3分钟码出并AC,只能恨自己赛时为什么这么sb...
D
模拟 + 差分
从左到右枚举\(i\),每一次枚举可以计算出第\(i\)个人有多少颗糖(\(a[i]\) + 前面\(i-1\)个人给的糖数\(num\))。这样,第\(i\)个人会给出的糖数为:
numout = min(n - i, a[i] + num)
给后面的人糖的区间范围是\([i + 1, i + numout]\)。可以考虑设置差分数组\(stop\),其中\(stop[i]\)表示枚举到第\(i\)个人时,前面\(i-1\)个人刚刚开始停止给第\(i\)个人糖的人数。这样每次可以令\(stop[i + numout + 1]++\),用\(sumstop\)维护差分数组的前缀和,就是当前\(i-1\)个人不给第\(i\)个人糖的人数。所以上述的\(num\)便可直接计算:
num = i - 1 - sum_stop
最后每个人的糖数即为:
max(0 , a[i] + num - (n - i))
具体细节见代码
code
E
贪心 + 思维,最想抽自己的一集
只需要注意答案数量不超过\(n/2\)。这样就能得到一个关键性质:前\(n/2\)个蛋糕一定放上面,后\(n/2\)个蛋糕一定放下面。(赛时因为没想到这个而坐牢\(1h\)...)
然后就没有然后了,直接前一半与后一半贪心匹配就好了,二分\(or\)双指针均可做。
code
G
二分 + ST表 + 推公式
这道题不能延续题解中E的做法,需要换一个思路。其实E还有另一种做法:
二分答案,然后取答案作为前后缀长度,这样转化为判断前后缀是否一 一匹配(证明不难,略)。
前后缀一 一匹配在E中可以线性双指针来判断,在G中就不行了,因为要对\(q\)个子数组判断,复杂度\(O(nq)\),故需要用一种更高效的方式来判断:
预处理\(a\)数组中每个位置\(i\),满足:\(a[T[i]] >= 2 * a[i]\)的第一个位置\(T[i]\)(\(1 <= i,T[i] <= n\))。
对于询问区间\([l,r]\),设二分的前后缀长度为\(k\),则对于前缀中的某个位置\(i\)(\(l <= i <= l + k - 1\)),设在后缀中需要匹配的位置为\(pos\),则有:
r - pos = l + k - 1 - i
即\(pos = r - l - k + i + 1\)。由于需要满足\(a[pos] >= 2 * a[i]\)的性质,故:
T[i] <= pos = r - l - k + i + 1
将带\(i\)的式子移到不等式左侧,即为:
T[i] - i <= r - l - k + 1
这个不等式是对任意 \(l <= i <= l + k - 1\) 均要满足的,否则就会出现前后缀中某个位置失配的情况,而右侧为定值。故可写作:
max(T[i] - i) <= r - l - k + 1 (l <= i <= l + k - 1)
因此,若上式满足,则当前二分的前后缀匹配,可以进一步扩大正在二分的答案\(k\);若不满足,则前后缀失配,需要缩小正在二分的答案\(k\)。
可以发现左边是求区间最大值,由于无修改,故可以用ST表预处理来维护区间\(T[i]-i\)的最大值,且ST表的查询复杂度为\(O(1)\),相对于线段树可以少一个\(log\)复杂度。
总时间复杂度为\(O(qlogn + nlogn)\)
code