【杂题乱写】Codeforces Round#905

由于是解题报告不是过题报告,所以理所当然的放弃了后三题代码的写。

感觉这场 Div1 D 是 cyh 的菜,E 是 gjk 的菜。我负责菜。

只写 Div1 题的题解。

A

双指针可以做 \(m=1\)

你发现随便换 \(a_1\) 答案最多减少 \(1\),而且 \(a_1\) 越趋向于减少,所以可以二分分割点

B

最短路,怎么 dijkstra 呢?设 \(dis_x\) 表示到达 \(x\) 号节点的时间(在传送机序列上)

那么把每张图在序列上什么时候出现记下来,每条出边直接二分找大于 \(dis_x\) 的最小的就行啦。

C

原序列字典序最小和最小化差分数组字典序没什么区别。考虑进行修改 \(1\sim i\) 和进行修改 \(1\sim j\) 哪个得到的序列会字典序更小一些。(\(i<j\)

这其实就是看修改 \(i+1\sim j\) 对原序列的影响,如果第一个非零的位置是负数,后者就要更优。所以搞一个 std::map 这些单点修改造成的影响存下来,随时把前缀的 \(0\) 赶出去就可以得到哪个修改的前缀最好,然后模拟一下得到答案。

standings 上一堆线段树,笑死我了。

D

注意到 \(a\) 是一个排列,那么根据常见套路,设置一个阈值 \(x\),大于 \(x\) 的看成 \(1\),否则是 \(0\)

如果一个区间只有一个 \(0\)\(1\) 的分界线,那么这个区间是合法的。不难发现伴随着阈值的增大,每个数都会从 \(0\)\(1\) 变化一次,每个数变化的时候这个 \(01\) 序列的其他部分不变,可以使用 std::multiset 找到前面最靠后的的 \(1\) 和后面最靠前的 \(0\)。中间这一坨就是可行的询问区间,那么变成了矩形加,单点判断 0/1。

这就是大家都知道的树状数组问题啦。

E

不难发现这就是个二分图(每行当成点,每列当成点)。因为上来给了你 \(2n\) 条边,所以你一定可以找到一个长度为偶数的环

其实你发现你最终的目的是找到一个颜色互不相同的四元环。所以做法就是你在这个环上找两个点求问它们之间的边的颜色是啥,从这两个点中间把这个环拆开成左右两部分,因为一开始的边的颜色互不相同,所以如果这条新边哪边都不一样就递归这边当成子问题。

你发现找的两个点的距离是二分之环长能让递归的次数最少,正好 \(2^{10}=1024\ge 1000\)

F

考虑构造一个 \(nxt\) 数组,\(nxt_i\) 表示下一个和 \(a_i\) 一样的数的位置,如果求出来 \(nxt_i\),相当于得到了若干链,每条链给一个数值就行了。

不难发现:

  • \(nxt_n=n+1\)
  • 如果 \(r_i=r_{i+1}\) 那么 \(nxt_i\in [i+1,r_{i}]\)
  • 如果 \(r_i<r_{i+1}\) 那么 \(nxt_i=r_{i+1}\)

利用上面三条我们可以得到一些点值和一些区间

  • 如果 \(nxt_i\le n\) 那么这些 \(nxt_i\) 互不相同。
  • 对于 \(i>nxt_1\) 都有 \(j<i\) 满足 \(nxt_j=i\)

最后一条限制如果被忽略的话,那么通过贪心给 \(nxt_i\) 赋值,赋不了的全都干成 \(n+1\)

如果我们需要考虑最后一条限制,可以考虑枚举有多少个 \(nxt_i\) 选择了 \(n+1\)。不难发现对于确定的 \(k\) 选最靠右(右端点)的区间选择 \(n+1\) 一定不劣,剩下的元素仍然是依次贪心。

\(k\) 显然具有可二分性,找到最小的满足条件的 \(k\) 进行 \(nxt\) 的构造即可。jiangly好像有一个不用二分的代码,不知道做法是啥。可能是继续 optimize

posted @ 2023-10-23 22:01  没学完四大礼包不改名  阅读(144)  评论(11)    收藏  举报