1 条题解

  • 0
    @ 2025-11-18 11:52:48

    题解:Binaria

    题目分析

    问题核心

    给定一个长度为 (M = N - K + 1) 的 SMS 序列(由二进制字符串的滑动窗口和组成),求可能的原始二进制字符串的数量,结果对 (10^6 + 3) 取模。

    关键约束与建模

    1. SMS 序列定义:设原始二进制字符串为 (x_1, x_2, ..., x_N)((x_i \in {0,1})),则 SMS 序列的第 (i) 项为: [ s_i = x_i + x_{i+1} + ... + x_{i+K-1} ]

    2. 相邻项关系:对于任意 (1 \leq i < M),有: [ s_{i+1} = s_i - x_i + x_{i+K} ] 变形得: [ x_{i+K} = s_{i+1} - s_i + x_i ] 该式是核心递推关系,表明 (x_{i+K}) 由 (x_i) 和 SMS 序列的相邻项差值决定。

    3. 二进制约束:(x_j \in {0,1}),因此由递推式可得:

      • (s_{i+1} - s_i) 只能是 (-1, 0, 1)(否则 (x_{i+K}) 无法为 0 或 1)。
      • 若 (s_{i+1} - s_i = 1),则 (x_{i+K} = x_i + 1),故 (x_i = 0) 且 (x_{i+K} = 1)。
      • 若 (s_{i+1} - s_i = -1),则 (x_{i+K} = x_i - 1),故 (x_i = 1) 且 (x_{i+K} = 0)。
      • 若 (s_{i+1} - s_i = 0),则 (x_{i+K} = x_i)(值相同,暂未确定)。

    数据范围挑战

    • (N \leq 10^6),需设计 (O(N)) 或 (O(K + M)) 复杂度的线性算法,避免嵌套循环。
    • 核心难点:利用递推关系将原始字符串按 (K) 个“链”分组,每组独立求解,最终通过组合数计算总方案数。

    算法设计

    核心思路

    1. 分组策略:根据递推关系 (x_{i+K} = s_{i+1} - s_i + x_i),原始字符串的第 (j) 位可按 (j \mod K) 分组(共 (K) 组)。例如 (K=4) 时,分组为 ({x_1, x_5, x_9, ...})、({x_2, x_6, x_{10}, ...}) 等。
      • 每组内的元素由递推关系关联,独立于其他组,总方案数为各组方案数的乘积。
    2. 组内约束检查与求解
      • 对每组,根据 SMS 序列的相邻差值,推导组内元素的取值约束(必须为 0 或 1)。
      • 若组内存在矛盾(如推导得出某元素既为 0 又为 1),则总方案数为 0。
    3. 自由变量计数与组合数计算
      • 每组中,若所有元素的取值已被完全确定(无自由变量),则方案数为 1。
      • 若存在未确定的自由变量,需结合 SMS 序列的首项 (s_1) 计算合法组合数。

    具体步骤

    1. 预处理组合数

    • 预计算 (10^6 + 3) 模下的阶乘 (fact)、逆元 (inv)、逆阶乘 (inv_fac),用于快速计算组合数 (C(n, k))(表示从 (n) 个元素中选 (k) 个的方案数)。
    • 组合数公式: [ C(x, y) = \begin{cases} 0 & x < y \text{ 或 } x < 0 \text{ 或 } y < 0 \ \frac{fact[x]}{fact[y] \cdot fact[x-y]} \mod (10^6 + 3) & \text{否则} \end{cases} ]

    2. 分组处理与约束检查

    • 对每组 (i)((0 \leq i < K),对应原始字符串的第 (i+1) 位开始的组):
      • 遍历组内元素(按 (j = i, i+K, i+2K, ...) 顺序),利用 SMS 序列的相邻差值 (s_{j+1} - s_j) 推导元素取值。
      • 若差值为 1 或 -1,组内元素的取值被唯一确定,检查是否满足 0/1 约束;若差值为 0,元素取值与前一个相同(暂未确定)。
      • 统计每组中已确定为 0 的元素数 (tot0)、已确定为 1 的元素数 (tot1)。

    3. 自由变量与组合数计算

    • 首项 (s_1) 是前 (K) 个元素的和,即: [ s_1 = \sum_{i=0}^{K-1} x_{i+1} ]
    • 设 (free = K - tot0 - tot1)(未确定的自由变量数,即前 (K) 个元素中未被约束的位数),(fixed1 = tot1)(前 (K) 个元素中已确定为 1 的位数)。则自由变量中需选择 (t = s_1 - fixed1) 个设为 1,剩余设为 0,方案数为 (C(free, t))。
    • 若 (t < 0) 或 (t > free),方案数为 0(无合法选择)。

    关键细节

    • 约束矛盾检查:若组内推导得出某元素取值既为 0 又为 1,或取值超出 0/1 范围,直接输出 0。
    • 自由变量的独立性:所有自由变量均在前 (K) 个元素中,且各组的自由变量互不影响,因此总方案数为组合数 (C(free, t))(因各组自由变量的选择需满足 (s_1) 的约束,本质是整体组合,而非各组乘积)。

    代码关键模块解析

    1. 组合数预处理

    int fact[N], inv[N], inv_fac[N];
    int C(int x, int y) {
        return (x < y || x < 0 || y < 0) ? 0 : (1ll * fact[x] * ((1ll * inv_fac[y] * inv_fac[x - y]) % mod)) % mod;
    }
    
    // 预处理阶乘、逆元、逆阶乘
    fact[0] = inv_fac[0] = inv[1] = inv_fac[1] = fact[1] = 1;
    for (int i = 2; i < N; i++) {
        fact[i] = (1ll * fact[i - 1] * i) % mod;
        inv[i] = (1ll * inv[mod % i] * (mod - mod / i)) % mod; // 费马小定理求逆元
        inv_fac[i] = (1ll * inv_fac[i - 1] * inv[i]) % mod;
    }
    
    • 预处理范围覆盖 (10^6 + 10),满足 (N \leq 10^6) 的需求。
    • 逆元计算采用费马小定理,因 (10^6 + 3) 是质数。

    2. 分组约束处理

    int tot0 = 0, tot1 = 0;
    for (int i = 0; i < k; i++) { // 遍历 K 个组
        bool ok = false;
        for (int j = i; j + k < n; j += k) { // 组内元素:j, j+k, j+2k,...
            if (a[j] != a[j + 1]) { // 相邻 SMS 项不同,差值非 0
                if (abs(a[j] - a[j + 1]) != 1) { puts("0"); return 0; } // 差值非法
                ok = true;
                // 推导组内元素取值
                if (a[j] > a[j + 1]) { b[j] = 1; b[j + k] = 0; }
                else { b[j] = 0; b[j + k] = 1; }
                // 向前推导组内前面的元素
                for (int x = j - k; x >= i; x -= k) b[x] = b[j];
                // 向后推导组内后面的元素
                for (int x = j + k; x + k < n; x += k) {
                    if (a[x] == a[x + 1]) b[x + k] = b[x];
                    else {
                        if (abs(a[x] - a[x + 1]) != 1) { puts("0"); return 0; }
                        if (a[x] > a[x + 1]) b[x + k] = b[x] - 1;
                        else b[x + k] = b[x] + 1;
                        if (b[x + k] < 0 || b[x + k] > 1) { puts("0"); return 0; } // 超出 0/1 范围
                    }
                }
                break;
            }
        }
        if (ok) { // 组内元素已确定
            if (b[i]) tot1++;
            else tot0++;
        }
    }
    
    • 对每个组,找到第一个相邻 SMS 项不同的位置,以此为起点推导组内所有元素的取值。
    • 若组内所有 SMS 项均相同,则组内元素取值相同(自由变量,未计入 (tot0) 或 (tot1))。

    3. 组合数计算与结果输出

    printf("%d\n", C(k - tot0 - tot1, a[0] - tot1));
    
    • (k - tot0 - tot1) 是前 (K) 个元素中的自由变量数。
    • (a[0] - tot1) 是自由变量中需设为 1 的个数(满足首项 (s_1) 的和约束)。
    • 组合数结果即为总方案数。

    时间复杂度与空间复杂度

    • 时间复杂度:(O(N + K))。预处理组合数为 (O(N)),分组处理每组元素的总个数为 (N),因此总时间线性。
    • 空间复杂度:(O(N))。存储阶乘、逆元、逆阶乘数组,以及输入的 SMS 序列和组内元素取值数组 (b)。

    关键结论

    1. 分组思想:利用递推关系将原始字符串按 (K) 分组,各组独立求解,大幅降低问题复杂度。
    2. 约束推导:通过 SMS 序列的相邻差值,推导组内元素的取值约束,确保满足二进制条件。
    3. 组合数应用:自由变量的选择需满足首项和约束,总方案数为组合数 (C(free, t)),体现了“有限制的选择”问题的核心解法。
    • 1

    信息

    ID
    5437
    时间
    1000ms
    内存
    1024MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者