专栏文章
WC2025 Nim 游戏 题解
P11629题解参与者 3已保存评论 2
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 2 条
- 当前快照
- 1 份
- 快照标识符
- @miqe9idm
- 此快照首次捕获于
- 2025/12/04 03:22 3 个月前
- 此快照最后确认于
- 2025/12/04 03:22 3 个月前
题意
给非负整数序列的每一项都加上一个非负数使得所有数的异或和为 ,最小化加上的数的总和。构造最多 组方案。
算法 0
我会猜猜猜!第一问直接输出全局异或和,看看能不能通过特殊性质 B!
期望得分 。
算法 1
我会 !
显然要求两个数相等。如果两个数本来就相等就结束了,否则只能给较小数加一点。方案数都是 。
期望得分 。
算法 2
我会爆搜!
你可以直接爆搜 的取值求最小值,然后接着爆搜构造方案。但是显然这么搜得到的重复状态有点太多了,只能通过 。时间复杂度是 。结合算法 期望得分 。
实际上我们可以基于改变后的 不会超过 的性质设计一个 dp: 表示前 个数加完之后异或和为 的最小加的数的和,然后仍然搜索出方案。这里倒着搜即可确保每个决策都会得出一个方案,于是时间复杂度是正确的。暴力实现 dp 时间复杂度 。结合算法一期望得分 。
实际上我场上特判这个点的时候没有发现改变后的 可以超过 但是也通过了,怎么回事呢?哦原来是有 B 性质啊那我不会卡了。实际上根据后面的分析,有 B 性质的情况下值域就是 。
算法 3
我会 A 性质!
首先我们把出现 次以上的数的出现次数都削成 ,即出现或不出现。特判一下已经结束了的情况。可以发现此时除非只剩下一种数的情况,和原来的时候的第一问答案是一样的。我们特判原来就所有数都相等的情况即可。手玩发现此时需要把两个 分别变成 和 ,且没别的等价的方案了,方案数 。
考虑剩下的东西,排序后得到序列 满足 ,且 。我们需要最小化修改后的 。此时最优方案之一大概就是把所有东西两两匹配,把小的变成大的,所以此时贪心地把 变成 , 变成 ……即把 变成 即可。有一个问题是如果 的长度是奇数该怎么办,即最后剩了一项消不掉,记为 。此时我们可以借助其它的不与 相同的数,直接给它加上 即可,相当于给它二进制上的这一位赋值成 ,所以不会有问题。最小代价即为 。
至于构造方案,我似乎没有一个特殊的好写的做法专门做这一档。场上写这一档写了一个小时破防了,后来发现直接按上面的匹配写搜索会漏很多东西,所以我改进后的做法的这个搜子不太像人写的。所以结合前面的算法期望得分只有 。
算法 4
我会按位贪心!
场上我根据手玩 A 性质的构造方案得出了一个有意思的结论:对于排序后的序列 上的一个修改操作,如果有一种方案是把 改成一个 之间的数,这相当于一次对于每个 ,都先把一个值等于 的数改成 ,然后再做后面的操作。这会产生大量的方案。这也告诉我们,我们可以倒着做这些操作。如果需要一个 的数,而需要把一个 变成这个,则我们先把 变过来也是等价的。
这就引出一个贪心。我们按位决策,如果某一位上的异或和是 ,我们把一个 变成 。变哪个好呢?由上述思考,如果抛弃了更高的所有位(可以发现确实可以这样做),我们取这一位是 的所有数里最大的一个来改。因为如果要改能小的,其实也可以用改最大的这一步来替代。
只需要注意我们要求存在一个这一位是 的。如果这一位全是 就不对了。这正好符合我们的 B 性质!如下参考代码可以通过 B 性质的第一问,时间复杂度 ,随手实现排序可以多一个 。结合前面的算法可以获得 分。
CPPvoid Main() {
cin>>n>>m;
REP(i,0,n)cin>>a[i];
vector<int>b;
REP(i,0,n)b.pb(a[i]);
c=0;
for(int i=59;i>=0;--i){
int f=0;
for(auto &j:b)if((j>>i)&1)f^=1;
if(f){
sort(all(b));
int x=lbound(b,1ll<<i)-1;
c+=(1ll<<i)-b[x];b[x]=0;
}
for(auto &j:b)if((j>>i)&1)j^=1ll<<i;
}
cout<<c<<endl<<0<<endl;
}
这玩意同时直接得到了一组方案,可以通过 的构造方案。结合前面的算法可以获得 分。这大概也就是包含笔者的 个 的由来。
算法 5
我会爆搜性质 B!
我们需要技巧性的快速爆搜。注意到上面用过的一个性质:我们处理完每一位之后可以直接扔掉这一位!这也就是说一个合理的方案最多只会用到 个操作。
考虑刚才分析“只操作最大的那个数”的结论,如果我们操作了一个更小的数,然后把它拆成了若干个步骤:。此时对当前这一位有影响的实际上只有最后一个改变 的操作是把这一位的 变成 的。于是确实可以看成,每一位只做一个操作,而且是把后 位清空,把第 位置成 。
我们基于这个结论搜索,并基本确保当前搜索到的序列 是有最小代价解的。注意到如果某一位选择的是操作后 位是 的,有解,则选择操作后 位比 更大的一定也有解。于是可以得到如果 无解,则比 小的也无解。我们按所有数的后 位排序,然后依次尝试修改这一位,然后搜索函数反悔“当前状态搜下去是否有解”,如果遇到无解了就直接退出即可。
考虑排序的次数,我不太会证明,也不太会感性理解根据题解时间复杂度也许是 ?这个太神秘了,但是确实跑得很快。结合前面的算法期望得分 。
注意如果某一位是不需要操作的我们就不能去遍历每一位了,否则会在 #18 获得 TLE 的好成绩。所以尽量实时维护异或和。
这个问题是我写完无特殊性质之后才发现的,所以只能提供一个 TLE on #18 的代码。
注意你需要提前把最小代价求出来。然后其实特殊性质 A 大部分时候是被 B 包含的,这个代码理应也可以通过,但是仍然要特判 是奇数且所有数相等,具体见算法 的第一部分。但是我很懒没写这个部分。
算法 6
我会正解!
考虑没有特殊性质 B 的情况。根据上面的分析,这个做法只会在 为奇数且某一位全都是 的时候会寄。
这个东西还有一个特性:之前的每一位异或和都是 。否则一次操作一定会清空某个数的后 位,然后这种情况就不存在了。
这个时候我们显然至少要把一个 改成 ,而我们只能用加法,所以麻烦的是这会影响更高位(而把 变成 不会这么麻烦,感性理解这当然是严格更优的,所以我们非必须不把 改成 )。
我们声称在最终方案中,第一个有过修改的位一定是有两个 变成了 。首先必须得有修改的,其次不能影响个数的奇偶性。所以只需要考虑是为什么不是把 变成 。其实很好理解,如果这样了就一定有了进位,那后面总有位变大了。即使上面每一位都只有 个 ,我们也可以取值域外的 这一位,所以肯定是有 内的解的。
然后做法就很直接了,直接枚举是哪一位操作了两次,然后由于这里直接会有一步清空,下面就不会再出现这种情况了。在求最优解的时候,直接取后 位最大的两个数操作,原理是同理的。在爆搜方案的时候,我们先从大到小枚举第一个操作的数。对于第一个数确定的情况,我们按上面的策略,第二个数从大到小,到无解为止。到第一个数对应的所有第二个数都无解的时候就退出。
时间复杂度也很神秘,据说是只需要多乘一个 ,也就是 。官方题解是这么写的,但是我也不知道为什么 那项没多乘一个 ,反正它还是跑得很快。期望得分 分。
相关推荐
评论
共 2 条评论,欢迎与作者交流。
正在加载评论...