经典思想:按位与操作的特点就是不同的二进制位互不影响,因此考虑把数字转化成二进制位,拆开考虑不同的位。异或也有类似的性质。

对于第位,此位的值为 ,则必定存在一个 (),满足: , ; ,

,则:

那么现在的问题就变成了:方程 , 有多少组不同的解?

我们设 表示确定了 以后, 的方案数。

那么, (和式的上界是)

答案即为:

注意到当 时, 只取决于 ()

() 的值仅取决于 ()

() 的值取决于 ()

所以一共只有 个状态是有用的,因此时间复杂度为 ,实现时使用滚动数组优化即可。

出题人感言:当我看见 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;
}