思路讲解
只要想到dp,那么写起来还是挺简单的。
这个dp的定义还是挺简单的,我的dp[i]的定义是以 i 的 mx 为结尾,所能得到的区间长度(计数),dp2[i] 的定义是以 i 的 mn 为结尾,所能得到的区间长度(计数)。
然后下划线部分注意初始化。
1 | inline void __(){ |
AC代码
https://www.codechef.com/viewsolution/1184464744
1 | // by Gospel_rock |
只要想到dp,那么写起来还是挺简单的。
这个dp的定义还是挺简单的,我的dp[i]的定义是以 i 的 mx 为结尾,所能得到的区间长度(计数),dp2[i] 的定义是以 i 的 mn 为结尾,所能得到的区间长度(计数)。
然后下划线部分注意初始化。
1 | inline void __(){ |
https://www.codechef.com/viewsolution/1184464744
1 | // by Gospel_rock |
https://www.hello-algo.com/chapter_divide_and_conquer/hanota_problem/#2
主要dfs代码。

1 | void dfs(ll rem,vll &src,vll &buf,vll &tar,char s,char b,char t){ |
1 | // by Gospel_rock |
题意就是计数问题,求满足∀i∈[2,N], Pi−1+Pi≡0(modK) 的排列有几个?
首先,看题面,我们发现 ∑i=1TNi≤2e5,因此最终算法是O(nlogn) 或者 O(n)。
然后我们猛然发现,K=3,确实,我们觉得束手无策是对的,因为如果没有这个条件,是3000+的题目。
那么看着这个数据,我们容易想到dp,dp[i] 表示1⋯i 这个排列有可能的总数量(状态定义与答案保持一致)。
但是我们发现这个转移还是比较难的,因为我们用插入一个数字的想法的话,不仅就是说这个原来的合法序列上能够插入,原来的非法序列上也能够插入,变为合法序列,这就导致dp数组存储的信息没有什么意义。
因此,我们还是回到组合数学上,可以令除以3之后余数为0,1,2的分别为r0,r1,r2。
不难发现,r0 和 r0 不能放在一起,r1 和 r2 也不能放在一起。那么总体结构类似于 r1∣r0∣r2 或者 r2∣r0∣r1 。
不难发现,如果我们直接固定 r0,接着去填充 r1 和 $r_2 $,这相当于解决:
有r0+1个盒子,其中有r0−1个必须装有球,且一个盒子装的球必须为同一种(一共只有两种),注意,可以理解为球带有编号,且放入盒子的顺序是重要的,盒子也是不同的。
这个问题是比较难解决的,不如我们先确定r1和r2,然后填充 r0 这样会简单很多。
1 | // 我们按照交替进行分类,就是1-0-2-0-1是可以的,1-0-1-0-2,我们按照1-0-2处理。 |
将1-0-1-0-2和1-0-1-0-2视为相同(r1的位置可以重新排列,但这属于重复计数)的缺点。实际上,如果我们在最后乘以P(r1,r1),我们就将r1的块视为相同。当然,r2也是相同的。
1 |
就是特判一下全部为1的情况(比较类似于反常nim),这个时候是可以分出胜负的。其他情况的胜率都是1/2。

https://www.codechef.com/viewsolution/1183760784
1 | // by Gospel_rock |

不难发现,可以把原来的问题转化为图论问题,就是要联通嘛,然后搞一个mst。
不难发现,num⊕(num−lowbit(num)) 是最小的,因为这个数是比你小的数里面,和你重叠程度最大的。
通过打表等手段,我们可以得到如下程序。
1 | ll ans=0; |
唯一就是这一部分不是很优雅。我不太喜欢。至少我们要搞搞清楚为什么要-1吧?
其实要回答这个问题也简单,就是一开始我们写程序的时候假设都是奇数,使用的都是奇数的方法进行计数的,但是不是2的幂次的偶数比较特殊,不能和奇数直接互相转化,需要-1(比如说6,7-6代价是1,但是没有7的时候,就没有这个代价,所以要-1),而2的幂次因为和1相连代价+1(原来1-4,代价为5,但是改为5-1,代价为4,4-5,代价为1,正好抵消)。
https://www.codechef.com/viewsolution/1183885336
1 |