答案显然具有单调性,因而总体解法是二分答案。
对于每个数字x而言,它可以被自身贡献一次,也可以根据题述变化规则由某些更大的数字贡献得到。
当x是偶数时, x x x会被 x + 1 x + 1 x+1和 x ∗ 2 x * 2 x∗2贡献一次;x是奇数时, x x x会被 2 ∗ x 2 * x 2∗x贡献一次。
因而我们自然而然地可以画出贡献树,(甚至)可以用矩阵快速幂可以知道每一层的节点数和fibonacci数列的关系…之后我就在假算法的路上越走越远…企图利用层数与节点的关系作为充分条件和必要条件进行递归暴力搜索&剪枝。但是对于那些不能剪枝的情况而言,其时间复杂度为fib数列接近于幂次情况,T飞。
解决办法在于从另一个角度看贡献树:
我们考虑对于每一个k,可以对n有贡献的
k
∗
n
+
b
k *n + b
k∗n+b的个数。
- 当n为奇数时: n , 2 n , 2 n + 1 , 4 n , 4 n + 1 , 4 n + 2 , 4 n + 3 , … n, 2n, 2n + 1, 4n, 4n + 1, 4n + 2, 4n + 3, \dots n,2n,2n+1,4n,4n+1,4n+2,4n+3,…
- 当n为偶数时: n , n + 1 , 2 n , 2 n + 1 , 2 n + 2 , 2 n + 3 , 4 n , 4 n + 1 , 4 n + 2 , … , 4 n + 7 , … n, n + 1, 2n, 2n + 1, 2n + 2, 2n + 3, 4n, 4n + 1, 4n + 2,\dots,4n + 7,\dots n,n+1,2n,2n+1,2n+2,2n+3,4n,4n+1,4n+2,…,4n+7,…
由此,规律便显而易见了。
此外,二分答案的关键在于答案的单调性。容易知道答案并不是整体单调的,而是分奇偶项分别单调,因而二分查询时应该进行分奇偶讨论;其中要仔细注意二分上下限的更新问题。
部分代码:
//求符合条件的kx + ?的个数
ll sove(ll x, ll k){
if(k * x > n) return 0;
ll left = n - k * x;
if(x & 1) return min(left, k - 1) + 1 + sove(x, k << 1);
else return min(left, 2 * k - 1) + 1 + sove(x, k << 1);
}