我复活辣!

D1T1 - 分糖果

题目大意.

个元素,每个元素 有一个"容量" ,一开始元素全为 。你要支持 次操作:区间加一个整数 (可以为负),然后每个元素与 取 max,与 取 min(这个过程下面称为校正)。

最后你要输出所有元素的值。

。时限 4s。

硬分块即可,不多讲了。


然而有个十分合理的一只 log 做法:

想象每个元素对应一个下标相同却没有校正过程的"自由元素",然后我们把原元素和其对应自由元素比较。问题就变成:

如何确定每个元素最后一次撞到上下边界是什么时候。

我们这里认为:初始状态算撞了一次下边界;从下边界横跳一下又碰到下边界不算是撞了两次,上亦然。

那么,两次撞边界之间的时间段中,对应自由元素的最大值和最小值之差必定 。而若某一时间段内,对应自由元素的最大值和最小值之差 ,那么必定有至少两次撞击。

于是也就容易用线段树定位出倒数第二次撞击了,进而得出最后一次撞击。

又可见 所用的线段树是十分相似的, 时直接小修一下即可。

D1T2 - 钥匙

题目大意.

现有 个房间、 条无向边和 类钥匙。每个房间中都有一把钥匙;每条边都需要你拥有指定的钥匙才能通过(钥匙不消耗)。

如果你一无所有地出现在 号房间(当然,你可以立即捡起 号房间中的钥匙),称你能到达的房间集合为

求使 最小的 。若有多个,请把它们全部输出。

这个问题问的就很有猫腻,努力想一想和它相关的做法。

观察 1. 显然,如果 能到达 那么 。于是,若 最小,那么对于任意 必然有

观察 2. 一旦发现两个点能互相到达,那么它们从此以后永远等价。

于是考虑维护一些互相等价的块,这些块之间构成一个 DAG。而我们想知道的是每个节点能否再扩展出边:如果形成环就说明这个环应当缩掉。

听起来很美好,但是怎么扩展边需要精细一点的考虑。

当然实际上我们也不需要 DAG 了,一条链就行。

代码好像没什么意义,先鸽为敬。

D1T3 - 喷泉公园

题目大意.

二维平面上有 喷泉,坐标各为 。保证 皆为偶数。

你要摆放 道路。每条道路都是一个长度为 的横向或纵向的线段,且必须恰好连接两座不同的喷泉。你要保证你的道路使所有喷泉连通。

对于每条道路,都要分配一个长椅。对于道路 ,长椅可以摆放在 ;横向的道路类似。道路不能共享长椅

请判断:是否存在一种合法的建造道路和长椅的方案。如果存在,你还需要输出任意一组解。

有个 Subtask 是没有 的喷泉方格,这背后肯定有猫腻。

打开 mspaint.exe,随便弄一组样例,然后瞎画一下,你会发现有个策略非常 nb:把长椅安置点黑白染色,白的只给横路用,黑的只给竖路用。这种情况只会在有两条紧挨的横/竖路时无法成功分配,然而它不会出现。

那么现在考虑如果有 的喷泉方格该怎么办。回忆:白格不能有两条横边,黑格不能有两条竖边;我们要毙掉一条。结果发现按精湛细腻的顺序依次毙就做完了。

D2T1 - 修改 DNA

题意懒得写了,思博题。

D2T2 - 地♂牢游♂戏

题目大意.

现在有一个 个房间的黑暗深邃的地牢和 个敌人,第 号敌人恰处在 号房间, 号房间是出口。

一个敌人有四个属性 。主角有一个属性,称为能力值。进入房间时会遭遇战斗:

  • 如果主角当前的能力值大于等于这个敌人的 ,那么主角会战胜它(但是敌人不消失,再次进入时还是会遭遇战斗),其能力值 ,并进入房间 。保证 大于当前房间编号。
  • 否则主角会战败,其能力值 ,并进入房间 。保证

显然游戏总是以主角走出地牢告终。现在有 组询问:若英雄初始能力值为 ,一开始进入的房间为 ,问主角走出地牢时的能力值是多少。

保证 大于当前房间编号我可以理解,算是保证游戏必定结束;但 一人身兼多职就很有问题了,这背后肯定又有猫腻。

答案是:如果有一个怪原来打不过现在打过了,那打完之后你的能力值和原来比至少翻倍了。也就是说这种情况最多发生 次。

然而尴尬的是我们难以得知这 次究竟是打败了谁,也不知道当时的能力值是多少。于是我们干脆把所有怪按 分段,建出能力值为 时的图(一个基环树森林)。


最后我们遇到了一个尖♂锐的问题:上面这个做法,空间 ,算一下常数就会发现过不去(倍增要记路径上的总和、最小值、目的地)……自然是用时间换空间,改成按 分段,每次晋级最多要击败 次。反正小力调参就行了。

代码如下。

D2T3 - 位移寄存器

题目大意.

现在你有 位二进制数,称为寄存器,你可以对这些寄存器做如下几种操作:

  • move 操作:把某个寄存器的值复制到另一个寄存器中。
  • store 操作:把某个寄存器的值赋为一个由你指定的常数。
  • not/left/right 操作:把某个寄存器按位取反/左移指定常数次/右移指定常数次(移出范围地的位直接消失)的结果赋到另一个寄存器中。
  • and/or/xor/add 操作:把某两个寄存器按位与/按位或/按位异或/加(这里的加法模 )的结果赋到另一个寄存器中。

你要完成以下两个任务:

  • 现有 位二进制数,按顺序存储在 号寄存器中,你要在 步操作内找到其中的最大值。
  • 现有 位二进制数,按顺序存储在 号寄存器中,你要在 步操作内将它们排序。

参考资料:这里

观察 0. 巨大位数的寄存器,自然提醒我们要并行处理。

观察 1. 我们没有 if。看来得强行搞点数字魔术了。(上期回顾

Part 1 - 两个数字的 max 和 min

让我们仔细想一想,有没有什么办法能不用 if 搞出 max 和 min。一个看起来比较有前途的方法是

那么问题就变成求绝对值

顺便,这样得到的 实际上告诉了我们原数列的大小关系,也就提示了一个常数更小的做法。

完 全 胜 利

然而还是过不去!怎么办?

Part 2 - 并行求 max 和 min

其实就是并行求绝对值。

常数大点没关系,反正是并行啊!!!这效率多顶。

对于原题,我们只需要把奇数编号的元素和偶数编号的元素取 max 得到新序列,再继续取 max 下去。这还提示我们不用手动添加符号位:原来放偶数编号元素的位置可以给我们随便污染。

Part 3 - 排序问题

考虑把原序列分成两半 并应用上面的神奇"并行 max/min",这样 。自然想到 是原来下标为奇数的所有元素, 反之。也就是把所有 摆正。

结果发现这种排序的第二轮是原地 tp,那么想到把 反一下再移一位(把所有 摆正)。一看这个排序好像还真的挺有道理,于是就过了。

下面瞎实现了一下,sub0 极限数据 128 步,sub1 极限数据 2887 步。