经典思想:按位与操作的特点就是不同的二进制位互不影响,因此考虑把数字转化成二进制位,拆开考虑不同的位。异或也有类似的性质。
对于第位,此位的值为
,则必定存在一个
(
),满足:
,
;
,
。
令,则:
那么现在的问题就变成了:方程 ,
,
有多少组不同的解?
我们设 表示确定了
以后,
的方案数。
那么, (和式的上界是
)
答案即为:
注意到当 时,
只取决于
(
)
而 (
) 的值仅取决于
(
)
(
) 的值取决于
(
)
所以一共只有 个状态是有用的,因此时间复杂度为
,实现时使用滚动数组优化即可。
出题人感言:当我看见 B 题没人交 C 题 20min 被秒的时候我就感叹我是良心出题人
C 题应该思路要比 B 自然清新一些。思想套路也比较经典,应该是比较偏向 NOIP 考察方向的一道题
题面第一句话取自长沙市麓山国际实验学校校歌(长郡中学寄宿部)“湘水滔滔流,岳麓青又青”,以纪念出题人在该校短暂而美好的一段高中快乐时光。毕竟后来回到长郡很难找到这么快了的时光。
联合出题人:麓山国际/长郡中学 Tony102
代码巨短
#include <bits/stdc++.h> using namespace std; const int SIZE = 5e6 + 5; const int mod = 1e9 + 7; int n, m; int f[SIZE]; namespace ae86 { const int bufl = 1 << 15; char buf[bufl], *s = buf, *t = buf; inline int fetch() { if (s == t) { t = (s = buf) + fread(buf, 1, bufl, stdin); if (s == t) return EOF; } return *s++; } inline int read() { int a = 0, b = 1, c = fetch(); while (!isdigit(c))b ^= c == '-', c = fetch(); while (isdigit(c)) a = a * 10 + c - 48, c = fetch(); return b ? a : -a; } } using ae86::read; int main() { n = read(), m = read(), f[m] = 1; while (m) { int sum = 0; for (int i = std::min(n, m); ~i; -- i) sum = (sum + f[i]) % mod; for (int i = 0; i <= m; ++ i) { int tmp = (sum - f[i]) % mod; if (i + n + 1 <= m) tmp = (tmp + f[i + n + 1]) % mod; f[i] = sum, sum = tmp; } for (int i = 0; i <= (m >> 1); ++ i) f[i] = f[i << 1]; m >>= 1; } printf("%d\n", (f[0] + mod) % mod); return 0; }