关羽下象棋
idea, solution from vfleaking, data from hehezhou
大家好,vfk 又来出题了 233 不过好像这次 FST 得有点惨。。哎,感觉这次出得不太好。
这道题既可以是一道猜结论题或者分类讨论题,也可以是一道暴力美学题。如果是我的话,在考场上我可能会当作暴力美学题来做,因为我会比较紧张;如果是平时自己做着玩的话,我会当一道分类讨论题来做。不过看到很多选手选择了分类讨论,却因为漏了情况而丢了不少分,我感到很惋惜。。
如果你回顾早年 UOJ 的 A 题,会发现都是些猜结论的题,而且还都有大牛十分钟就过了(但这道象棋题比以往的猜结论题复杂了许多)。我确实比较喜欢这类题吧。。因为我自己在赛场上有可能无法在短时间内那么机智地猜出结论,也许只会在做了一个小时之后才慢悠悠地提交上我的程序,所以我是真心觉得十分钟就过了当年猜结论类的 A 题的人很厉害。我一直觉得猜结论的能力本质上是一种计算机科学的直觉,或者数学直觉(类似于物理老师们强调的物理直觉)。真实世界的问题远比 OI 复杂,往往没有办法直接就做出来。很多时候确实是先得有人猜出来一个大致解法,然后再慢慢证明。所以说咯,直觉确实是个很重要的东西。
OIer 出比赛可能会有很多种不同的目的,比如展示自己的水平,比如分享自己的新想法,比如退役纪念。我的话,除了选题造题本身带给我的成就感之外,我会把比赛当作一种交流的途径。所以咯,当时我很喜欢把别人投来的猜结论题放到 UOJ 比赛里。大概想法就是,虽然我知道我有可能只会慢悠悠地做出来,但我就是想看看高手是怎么思考的,并学习学习。
不过这题不是别人投来的哈哈,我不确定我是否真的有出一道难度适中的猜结论题的水平,感觉这次有点翻车 ovo 啊所以,顺便征个题 233 欢迎来投小清新猜结论题呀!
好了下面我来讲讲我的思路(但并不是完全严谨的证明),也欢迎赛场上 AC 的同学们发发博客交流讨论。赛场上第一个用分类讨论的方式 AC 的应该是 Flying2018(提交记录链接),还有一位是 Foden47(提交记录链接)。所以强的人还是强啊!
算法一
第一个子任务里,车和卒是同一行的,显然车只要直接把卒吃掉即可。我猜肯定会有不想做博弈题直接跑路的选手 🤣 跑路之前这个部分分还是得拿一下呀,20分呢。
算法二
根据算法一的思路,如果卒和车在同一行或者同一列,我们直接输出
诶,我们接着来看第……第……第四个子任务!为什么直接跳到了第四个呢,因为我们需要想想一般情况下这个题的答案到底是多少。想要分类讨论,就要先想想答案最大是多少。而在第四个子任务里,卒和车都在棋盘靠中心的位置,所以我们至少可以不用考虑棋盘边界对答案的影响。
先看有没有对称性可以利用 —— 诶,有。卒和车都是左右对称的,所以我们不妨设
现在我们随手画一个车在卒右边的情况:
诶,然后我们来试试怎么把卒吃掉。我们先跑到卒的下面一行,这样卒就不敢往下走了,只能左右乱窜。然后我们就像样例一的第二组数据一样做:在卒的下面一行撒一行毒雾出来,卒仍然只能左右乱窜;现在我们跑到卒所在的那一行,卒仍然只能左右乱窜;接下来我们就把它吃掉。这样我们就得到了一个四步将卒吃掉的策略。
努力枚举一番,你会发现你不可能找到一个三步的策略。等等,但为什么样例里最大值只有 3 呢。。。这确实是我们故意的,我们只放了答案小于等于 3 的情况。我们的本意是想区分开对着样例调的选手,和从头到尾先自己想再用样例检查正确性的选手,没想到导致了大片的 FST。(俗话说,出题人的嘴,骗人的鬼,所以还是不要轻信样例啊)
思考下这个策略,你会发现无论位置如何,你总能使用该策略。棋盘边界并不会有什么坏的影响,只会进一步压缩卒的移动空间,让答案小于等于
不过我们现在在做第四个子任务,所以我们先不考虑边界的问题。那么,只要不在同一行同一列,就肯定是 4 了吗?如果卒和车离得很远的话,确实没有任何办法再优化 —— 首先如果卒既可以横向走,也可以向下走,那么卒肯定没办法在一步内被你吃掉,所以在吃掉卒之前,我们要么用毒封住卒的左右两侧,要么用毒封住下面。但封住左右两侧是需要走很多步的。因为如果你先封左边,卒可以往反方向走,导致你只能努力把它卡在某两列或者某三列,然后它就可以在里面继续乱走。经过一番尝试你会发现先封左侧或者先封右侧都是不可能比 4 步更优的。如果要封下面,那么晚封不如早封,所以就变成上图的那个策略了。
所以就是要考虑卒和车靠得很近的情况。可以发现,只要初始的时候不在卒所在的下一行,好像都没什么意义,还是得走到那行放毒。。如果在卒所在的下一行,那么上图的策略就可以省掉第一步了,于是 3 步就可以吃掉卒。(当然你看样例也会发现有这个情况)
特判掉这个情况,就可以过第四个子任务了。
if (x1 == x2 || y1 == y2) {
return 1;
}
if (x2 == x1 + 1) {
return 3;
}
return 4;
结合算法一,期望得分 40 分。
算法三
通过上述思路理清楚答案不超过
因为答案不超过
所以我们就可以在卒的初始位置附近,向各个方向延申常数格(仔细想想会发现常数取
好的,迭代加深!博弈搜索!游戏规则也不是很复杂,按照题面把代码写出来就好了!赛场上大部分 AC 的选手正是用的这一暴力美学。
要注意的是搜索时我们要标记哪些位置是车曾经走过的。如果用布尔数组来存储,回溯时不方便还原。所以你可以用 int 数组存储每个位置被车经过的次数,这样递归时加加,回溯时减减就好了。
至于时间复杂度,你可以把所有可能的数据都试一遍,就会发现跑得贼快。这样大胆交一发,就直接过了。如果你使用的是这种做法,本题难点其实是你如何在尽量短的时间内,写出一份正确的博弈搜索代码。我看到有些选手的实现方式十分简洁,大家可以找来看看 233
一种实现下,搜索树的一个上界是
期望得分 100 分。
算法四
下面我继续讲讲怎么通过分类讨论解决本题。
算法二已经解决了远离边界的情况,所以下面我们只用考虑边界问题。
由于卒不能往上走,所以上边界并没有什么用。而左右边界又是对称的,所以我们只用考虑左边界和下边界。(回忆下我们之前已经讨论过,只用考虑
思路大致如下。我们已知答案小于等于 4,而 1 的情况已经被我们判掉了,所以我们只用找出那些答案为 2 或者 3 的情况,剩下的就是 4。
首先思考一下什么情况会出现
那么只有两种可能。要么卒的下边是棋盘边界,要么卒的左边是棋盘边界,如下图所示。一种情况是卒在最后一行,只能左右走。此时车走到卒所在的那一行,然后再花一步吃掉就好了。另一种情况是卒在第一列,比如
还有别的情况吗?如果卒在第一列且不在最后一行,车跟
综合上述讨论,虽然我们原本是想解决一下所有 2 步吃卒的情况,但似乎我们顺带解决了所有卒在第一列或者最后一行的情况了。这样就解决了第二和第三个子任务。
if (x1 == x2 || y1 == y2) {
return 1;
}
if (y1 > y2) {
y1 = m - y1 + 1;
y2 = m - y2 + 1;
}
if (x1 == n) {
return 2;
}
if (y1 == 1) {
return x2 == x1 + 1 || y2 == 2 ? 2 : 3;
}
if (x2 == x1 + 1) {
return 3;
}
return 4;
结合算法一算法二,期望得分 80 分。
算法五
我们离 AC 只有一步之遥了!这里只需要进一步讨论靠近棋盘边界的情况。但因为我们已经理清了答案小于等于 4 这一点,也找到了所有答案为 2 的情况,所以现在我们现在只用找出剩下的所有三步吃卒的情况就好了。
因为已经解决了第一列和倒数第一行的情况,所以我们只用接着考虑第二列和倒数第二行就好了。如果卒到边界的距离超过了 2 的话,只有卒在走第三步的时候边界才可能产生影响。然而我们讨论的是车是否可以三步吃卒,所以卒只能走两步。因此我们并不用考虑卒到边界的距离超过 2 的情况。
首先我们来讨论卒在倒数第二行的情况。我们只用枚举一下看看有没有三步吃卒的方案,结果发现还真有。我们只要把车挪到倒数第二行,那么卒如果左右动就会被立马吃掉,所以卒只能往下。这就变回了卒在最后一行的情况,我们只需要两步即可把卒吃掉:把车挪到最后一行,然后卒无论怎么动都会在下一步被吃了。
接下来我们来讨论卒在第二列的情况。类似卒在第一列的情况,我们可以注意到,如果卒在
除此之外,只要第一步不通过某种方式阻止卒往右走,好像卒就会跑到第三列,然后就很难用所剩的 2 步吃掉卒了。所以就没办法了?
我出题的时候,第一次分类讨论就想到了这么多。然后码了一个暴力美学,结果发现 WA 了!
我漏掉的是这个情况:
眼熟吗?眼熟。因为这是样例一的最后一组数据。我发现自己都漏了这个情况之后灰溜溜把这种情况加入了样例,防止大家 FST。。。哎但最后发现样例还是给得太弱了。
这个情况下也是 3!为什么呢?这种情况不是没法阻止卒往右走吗?
确实,但这种情况可以绕个圈把卒困住!我感觉这实在有点妙 233 可能也是这道题最有趣的地方了。首先车先走到卒下方那一行,然后卒只能左右走,否则就会被吃掉。然后无论往左还是往右,车都移动到第二列。如果卒是往左走的,那么下一步无论向右还是向下都会被一步吃掉;如果卒是往右走的,那么卒现在向右和向下都被死死地封住了,所以只能往左,结果就会被一步吃掉。所以样例里的这种情况,三步就能吃掉卒。
仔细想想,这种先围住然后再吃的情况只会发生在
综合上述所有情况,我们就可以写出代码,获得 100 分了:https://uoj.ac/submission/531552
写在最后
如果不是放在比赛时做,心平气和地分类讨论一番,再结合样例的提示,应该是可以一次写对的。个人也确实很喜欢这种抽丝剥茧的分类讨论,写对了还蛮开心的。但现在这么出导致了一大片 FST,好像确实不太好。。再次谢罪.jpg
张飞卷精兵
idea by LMoliver, vectorwyx, solution,std,data by hehezhou
算法一
爆搜所有安排方案。可以证明安排方案总种类数为
复杂度
算法二
考虑把对战建一棵树。每次对拼让赢家成为输家的父亲,点权即为初始卷力。那么答案就是奇数层权值之和减去偶数层权值之和。可以发现填数方案是合法的当且仅当父亲权值都大于儿子权值。考虑把权值从大到小填入,则这个过程可以看作:初始只有根解锁,一个被解锁的节点填入权值可以使它的儿子被解锁。
考虑一个贪心策略,如果当前能填入偶数层节点则直接填入,否则选一个节点填入,使得被解锁的节点数最多,如有多个任选即可。
这个策略是正确的!因为实际上如有多种选择,以这些节点为根的子树是同构的。考虑这棵树的构造方式。令
时间复杂度
算法三
考虑优化上述过程。将儿子个数为
删去一个奇数层
维护出当前解锁的奇数层每类节点有多少个,依次将
时间复杂度
算法四
考虑优化
时间复杂度
赵云八卦阵
idea by orbitingflea, solution,std,data by QAQAutoMaton
算法一
我会记搜枚举所有合法方案!
期望得分
算法二
设最后得到的序列为
注意到
对于每个
时间复杂度
算法三
我会dp!
dp[i][j]
表示前i个位置,长度为j的上升子序列,末尾的位置的最小值。
从i转移到i+1的时候枚举
算法四
我会线性基!
我们发现,
则我们可以在算法三的基础上,在线性基里查询满足
时间复杂度
算法五
注意到当
考虑我们对前一种情况整段转移。
由于有更新的dp值一定在线性基中,所以我们可以对原来的dp值算出在线性基里的排名,然后比它大的最小值就是排名+1,以此类推。
整段转移直接用单调队列优化即可。
时间复杂度
算法六
算法五的瓶颈在于在线性基里查询。
注意到任何时候
对于整段转移的部分,我们就可以直接+1,对于更新线性基的情况,我们需要把排名转为新的排名。
注意到把线性基写成等价的最简形式(即每个元素的最高位依次为
那么当一个数被插入线性基后,对旧元素的影响是让某些更高的位异或上了
接下来的单点转移,只需要求出最小的比某个dp值大的,满足某几位异或和是定值的数。可以类似的O(1)求出。
时间复杂度
算法七
by QAQAutoMaton
先从前往后把每个
对于一个没在线性基中的
所以只需要考虑在线性基中的i是否在lis中。
设在线性基中的位置为
可以发现相邻两个
考虑dp[i][j]表示只考虑
则我们整段转移的时候只需要定位到
时间复杂度
马超战潼关
idea by byqx, solution,data by he_____he , std by hehezhou
显然题意即为求一个经典二分图建图中的最小割个数。
算法 1
我会暴力!枚举每条边选或不选,检查是否是一个最小割即可。
时间复杂度
算法 2
我会比较好的暴力!令
枚举每条与源点和汇点相连的边是否在割中,就能知道中间的边是否需要割。
时间复杂度
算法 3
枚举左侧割了哪些边。对于右侧一个点,考虑有哪些左侧未被割开的点向这个点连边。如果总连边数量超过
时间复杂度
算法 4
换一种方式看待问题。考虑先求出一种最大匹配的方案。可以发现,对于其中一个匹配
枚举割哪条,看是否是个合法方案即可。
时间复杂度
算法 5
仔细观察上一个算法,考虑什么样的方案是合法的。构造一个图,每个点代表一对匹配,用点权
- 如果
与 都在匹配中,则相当于不能有 且 ,即 或 。 - 如果只有
在匹配中,则相当于要求 。 - 如果只有
在匹配中,则相当于要求 。
对于第一种情况,我们在
可以发现如果有一条边
枚举选
时间复杂度
算法 6
我会爆搜!每次挑度数最大点爆搜,出现不同连通块就分开计算。
时间复杂度:???
期望得分
算法 7
观察
再考虑图中的环。可以发现环上的所有点一定权值都相等,并且不能为
枚举前一半哪些选
时间复杂度
算法 8
现在每个点点权可以为
时间复杂度
算法 9
再仔细观察分为两半之后的过程。
如果只枚举前一半哪些选
- 前一半对后一半的影响
- 前一半内部的方案数
先看前一半内部的方案数如何求出。类似的,只有
再看前一半对后一半的影响。如果一个点为
时间复杂度
黄忠庆功宴
idea, solution, std, data from djq_cpp
这道题出处其实是 chasedeath 问我的一道多项式题(?)大家似乎不太资瓷今年 Goodbye 出多项式,但感觉这个求和的部分本身也挺趣味就出出来了。原定是 D,由于出题人实在出不出困难而有趣的 E 就直接放 E 了,向广大 OIer 谢罪(雾
算法 1(std)
每次查询即求下标为模意义等差数列的
考虑公差
进一步地,考虑将
算法 1.1
根据观察场上许多选手的做法都与算法 1 基本一致,但选取的 (虽然针对程序使用最劣的公差大概可以卡到多一个
(不过很多选手都因为常数问题只获得了
算法 2 一些想法
笔者并未发现与算法 1 本质不同的可 AC 做法,不过从欧几里得算法的角度看,原问题等价于每次求