1 条题解

  • 0
    @ 2025-10-16 11:54:40

    题解说明

    问题背景:
    这是某类矩阵计数题的实现。给定一个 n×mn \times m0101 矩阵(字符 '0''1'),需要根据题目要求计算两类答案 anscanscansfansf。参数 ccff 控制是否输出对应答案。最终结果对 998244353998244353 取模。

    核心思路:
    1.1. 预处理连续 00 的长度:

    • 用数组 d[i]d[i] 表示在当前列 jj 上,从位置 ii 开始向下连续 '0' 的长度。
    • 遍历时从下往上更新:若 s[i][j]=0s[i][j] = '0',则 d[i]=d[i]+1d[i] = d[i] + 1,否则 d[i]=1d[i] = -1

    2.2. 列扫描:

    • 从右到左枚举每一列 jj
    • 在每列中,从下到上扫描每一行 ii
    • 使用三个临时变量:
      • nowcnowc:累计的横向贡献
      • nowfnowf:累计的纵向贡献
      • cntcnt:计数器,用于控制 nowfnowf 的累加

    3.3. 贡献计算:

    • 若当前位置 d[i]>0d[i] > 0 且已有累计贡献 nowcnowc
      • ansc+=nowc×d[i]ansc \mathrel{+}= nowc \times d[i]
      • ansf+=nowf×d[i]ansf \mathrel{+}= nowf \times d[i]
    • 若当前位置不是 '0'd[i]=1d[i] = -1),则清空临时变量。
    • 否则若下一行 d[i+1]1d[i+1] \neq -1,说明连续 00 继续延伸:
      • nowc+=d[i+1]nowc \mathrel{+}= d[i+1]
      • nowf+=cnt++×d[i+1]nowf \mathrel{+}= cnt^{++} \times d[i+1]

    4.4. 输出:

    • 每组测试用例结束后,输出 (c?anscmodMOD:0)(c ? ansc \bmod MOD : 0)(f?ansfmodMOD:0)(f ? ansf \bmod MOD : 0)

    算法流程:
    1.1. 输入测试用例数 tt 和题目编号 idid
    2.2. 对每组测试用例:

    • 输入 n,m,c,fn, m, c, f
    • 读入 nn 行矩阵。
    • 初始化 dd 数组为 1-1
    • 从右到左枚举列,从下到上更新 d[i]d[i] 并计算贡献。
    • 输出结果。

    复杂度分析:

    • 外层循环 mm 列,内层循环 nn 行,总复杂度 O(n×m)O(n \times m)
    • n,m1000n,m \leq 1000 时可行。

    实现细节与注意点:

    • dd 数组初始化为 1-1,表示没有连续 00
    • 从下往上更新 d[i]d[i],保证连续 00 的长度正确。
    • nowcnowcnowfnowf 的更新逻辑要区分“断开”和“延续”两种情况。
    • 最终答案取模 998244353998244353

    总结:
    该代码通过逐列扫描和动态维护连续 00 的长度,利用临时变量累计贡献,最终在 O(n×m)O(n \times m) 时间内完成 anscanscansfansf 的计算。整体思路是“列枚举 ++ 连续 00 统计 ++

    ```cpp
    #include "bits/stdc++.h"
    using namespace std;
    const int MOD = 998244353;
    char s[1001][1002];  // 存储输入的字符矩阵
    int d[1002];         // 记录当前列中每个位置的连续0长度
    
    int main() {
        // 去掉文件重定向,保留cin加速配置(优化标准输入效率)
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
    
        int t, id, n, m, c, f, cnt, i, j;
        long long nowc, nowf, ansc, ansf;
        cin >> t >> id;  // 标准输入:测试用例数t和题目编号id
    
        while (t--) {  // 处理每组测试用例
            memset(d, -1, sizeof(d));  // 初始化d数组为-1(表示无连续0)
            cin >> n >> m >> c >> f;    // 标准输入:矩阵大小n*m,是否计算ansc和ansf的标志
            ansc = ansf = 0;            // 初始化当前测试用例的结果
    
            // 标准输入:读入n行字符矩阵(每行从s[i][1]开始存储)
            for (i = 1; i <= n; i++){
                string t;
                cin>>t;
                for(int j =1 ;j <= t.size();j++)s[i][j] = t[j-1];
            }
    
            // 从右到左遍历每一列,计算连续0的贡献
            for (j = m; j >= 1; j--) {
                nowc = nowf = cnt = 0;  // 临时变量:当前列的累计贡献
    
                // 从下到上遍历当前列的每一行
                for (i = n; i >= 1; i--) {
                    // 更新d[i]:当前位置是'0'则连续长度+1,否则重置为-1
                    d[i] = (s[i][j] == '0') ? d[i] + 1 : -1;
    
                    // 若当前有连续0且有累计贡献,累加结果
                    if (d[i] > 0 && nowc) {
                        ansc += nowc * d[i];
                        ansf += nowf * d[i];
                    }
    
                    // 重置逻辑:当前位置不是连续0,清空临时变量
                    if (d[i] == -1) {
                        nowc = nowf = cnt = 0;
                    }
                    // 累加逻辑:当前位置的下一行有连续0,更新临时贡献
                    else if (d[i + 1] != -1) {
                        nowc += d[i + 1];
                        nowf += cnt++ * d[i + 1];
                    }
                }
            }
    
            // 标准输出:根据c和f的标志输出结果(取模MOD)
            printf("%lld %lld\n", (c ? ansc % MOD : 0), (f ? ansf % MOD : 0));
        }
    
        return 0;
    }
    
    • 1

    信息

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