#P2907. 第1题-电影院选座
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 13
            Accepted: 5
            Difficulty: 5
            
          
          
          
                       所属公司 : 
                              阿里
                                
            
                        
              时间 :2025年4月24日-阿里淘天(开发岗)
                              
                      
          
 
- 
                        算法标签>模拟          
 
第1题-电影院选座
题意
给定一个 n 行 m 列的影院座位布局,其中 0 表示空座,1 表示已被订出。你和朋友想一起观影,且组内人员可区分。每次希望选定连续的 k 个空座,中间不允许被占,且在垂直方向(上下)和水平方向(左右)相邻的位置都不能有已订座位,满足条件的方案数乘以 k! 即为答案,对 998244353 取模。
输入包括多次询问,每次给出一个 k,需要输出对应的方案数。
题解思路
枚举每行安全区间
- 定义 
safe[i][j]:仅当座位 (i,j) 为空且其上下位置要么越界要么也是空时,safe[i][j]=1,否则为0。 - 对于每一行,用前缀和 
pref[j]快速判断任意区间内是否全是safe=1。 - 枚举长度 k,对每行滑动窗口判断区间 [j,j+k−1]:
- 区间内全为安全位:
pref[j+k]-pref[j]==k。 - 区间左右相邻横向位置(若存在)都不是已订座(
a[i][j-1]和a[i][j+k]不为1)。 
 - 区间内全为安全位:
 - 统计所有行符合条件的区间总数 
cnt[k]。 - 答案为 
cnt[k] * k! % MOD。 
复杂度分析
- 枚举每行:O(n)
 - 每行构造前缀和:O(m)
 - 对每个 k≤min(m,maxk) 遍历起点:最坏 O(m)
 - 总体:O(n×m×min(m,maxk))
 - 额外预处理阶乘 O(maxk),均在 n,m,q≤100 约束内可接受。
 
代码实现
Python 代码
import sys
input = sys.stdin.readline
MOD = 998244353
def main():
    n, m, q = map(int, input().split())
    a = []
    # 读入座位矩阵
    for _ in range(n):
        line = input().strip().split()
        if len(line) == m:
            row = list(map(int, line))
        else:
            # 连续字符
            row = list(map(int, list(line[0])))
        a.append(row)
    # 读入所有查询
    qs = list(map(int, input().split()))
    maxk = max(qs)
    # 预处理阶乘
    fac = [1] * (maxk + 1)
    for i in range(1, maxk+1):
        fac[i] = fac[i-1] * i % MOD
    # 构造 safe 数组
    safe = [[0]*m for _ in range(n)]
    for i in range(n):
        for j in range(m):
            if a[i][j] == 0:
                ok = True
                if i>0 and a[i-1][j]==1: ok = False
                if i<n-1 and a[i+1][j]==1: ok = False
                safe[i][j] = 1 if ok else 0
    maxk_seg = min(m, maxk)
    cnt = [0] * (maxk_seg+1)
    # 统计每种 k 的安全连续段
    for i in range(n):
        pref = [0]*(m+1)
        for j in range(m):
            pref[j+1] = pref[j] + safe[i][j]
        for k in range(1, maxk_seg+1):
            tot = 0
            for j in range(m-k+1):
                if pref[j+k] - pref[j] == k:
                    if j>0 and a[i][j-1]==1: continue
                    if j+k<m and a[i][j+k]==1: continue
                    tot += 1
            cnt[k] += tot
    # 输出结果
    res = []
    for k in qs:
        if k<=maxk_seg:
            res.append(str(cnt[k] * fac[k] % MOD))
        else:
            res.append('0')
    print(' '.join(res))
if __name__ == '__main__':
    main()
Java 代码
import java.io.*;
import java.util.*;
public class Main {
    static final int MOD = 998244353;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        int m = Integer.parseInt(st.nextToken());
        int q = Integer.parseInt(st.nextToken());
        int[][] a = new int[n][m];
        // 读入座位矩阵
        for (int i = 0; i < n; i++) {
            String line = br.readLine().trim();
            String[] parts = line.split("\\s+");
            if (parts.length == m) {
                for (int j = 0; j < m; j++) a[i][j] = Integer.parseInt(parts[j]);
            } else {
                for (int j = 0; j < m; j++) a[i][j] = line.charAt(j) - '0';
            }
        }
        // 查询列表
        int[] qs = Arrays.stream(br.readLine().split("\\s+"))
                         .mapToInt(Integer::parseInt).toArray();
        int maxk = Arrays.stream(qs).max().orElse(0);
        // 阶乘
        long[] fac = new long[maxk+1]; fac[0]=1;
        for (int i = 1; i <= maxk; i++) fac[i] = fac[i-1]*i%MOD;
        // safe 数组
        int[][] safe = new int[n][m];
        for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) {
            if (a[i][j]==0) {
                boolean ok = true;
                if (i>0 && a[i-1][j]==1) ok = false;
                if (i<n-1 && a[i+1][j]==1) ok = false;
                safe[i][j] = ok ? 1 : 0;
            }
        }
        int maxkSeg = Math.min(m, maxk);
        long[] cnt = new long[maxkSeg+1];
        // 统计
        for (int i = 0; i < n; i++) {
            int[] pref = new int[m+1];
            for (int j = 0; j < m; j++) pref[j+1] = pref[j] + safe[i][j];
            for (int k = 1; k <= maxkSeg; k++) {
                int tot = 0;
                for (int j = 0; j+k <= m; j++) {
                    if (pref[j+k]-pref[j] == k) {
                        if (j>0 && a[i][j-1]==1) continue;
                        if (j+k<m && a[i][j+k]==1) continue;
                        tot++;
                    }
                }
                cnt[k] += tot;
            }
        }
        // 输出
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < q; i++) {
            int k = qs[i];
            long ans = (k>=1 && k<=maxkSeg) ? cnt[k]%MOD*fac[k]%MOD : 0;
            sb.append(ans).append(i<q-1?' ':'\n');
        }
        System.out.print(sb);
    }
}
C++ 代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD = 998244353;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m, q;
    cin >> n >> m >> q;
    vector<vector<int>> a(n, vector<int>(m));
    string s;
    // 读入座位
    for (int i = 0; i < n; i++) {
        cin >> s;
        if ((int)s.size() == m && (s.find(' ') == string::npos)) {
            for (int j = 0; j < m; j++) a[i][j] = s[j] - '0';
        } else {
            // 不带空格时自动处理
            for (int j = 0; j < m; j++) a[i][j] = s[j] - '0';
        }
    }
    vector<int> qs(q);
    for (int i = 0; i < q; i++) cin >> qs[i];
    int maxk = *max_element(qs.begin(), qs.end());
    vector<ll> fac(maxk+1, 1);
    for (int i = 1; i <= maxk; i++) fac[i] = fac[i-1] * i % MOD;
    // safe 数组
    vector<vector<int>> safe(n, vector<int>(m, 0));
    for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) {
        if (a[i][j]==0) {
            bool ok = true;
            if (i>0 && a[i-1][j]==1) ok = false;
            if (i<n-1 && a[i+1][j]==1) ok = false;
            safe[i][j] = ok;
        }
    }
    int maxkSeg = min(m, maxk);
    vector<ll> cnt(maxkSeg+1, 0);
    // 统计区间
    for (int i = 0; i < n; i++) {
        vector<int> pref(m+1, 0);
        for (int j = 0; j < m; j++) pref[j+1] = pref[j] + safe[i][j];
        for (int k = 1; k <= maxkSeg; k++) {
            int tot = 0;
            for (int j = 0; j+k <= m; j++) {
                if (pref[j+k] - pref[j] == k) {
                    if (j>0 && a[i][j-1]==1) continue;
                    if (j+k<m && a[i][j+k]==1) continue;
                    tot++;
                }
            }
            cnt[k] += tot;
        }
    }
    // 输出答案
    for (int i = 0; i < q; i++) {
        int k = qs[i];
        ll ans = 0;
        if (k>=1 && k<=maxkSeg) ans = cnt[k] % MOD * fac[k] % MOD;
        cout << ans << (i+1<q ? ' ' : '\n');
    }
    return 0;
}
        题目内容
电影院共有 n 行、m 列座位。部分座位已被陌生人的购买,剩余座位为空闲。你和你的 k 位朋友希望一起观影,选座时有以下要求:
只能选择空闲座位;
全部 k 个人的选座需要位于同一行,且保持连续;
对于选中的每一个位置,其上下左右相邻的位置要么不存在,要么为空闲,要么同样为被你们选中的位置。
现在,给出电影院的座位示意图,以及多次询问,每次询问给出 k 个整教,表示你们希望一起观影的人数,请计算对于每次询问,有多少种不同的选座方案满足上述要求。两个方案不同当且仅当至少有一个人与之前的位置不同。若不存在任何合法方案,输出 0 。由于答案可能很大,请将答案对 998 244 353 取模后输出。
输入描述
第一行输入三个整数 n,m,q(1≦n,m,q≦100) ,表示电影院座位的行数、列数、询问次数。
此后 n 行,第 i 行输入 m 个整数 ai,1,ai,2,...,ai,m(0≦ai,j≦1),其中 ai,j ,表示电影院第 i 行第 j 列的座位状态。ai,j=0 表示空用,ai,j=1 表示已被购买。
第 n+2 行输入 q 个整数 k1,k2,...,kq(1≦ki≦n×m),表示每次询问中,你们希望一起观影的人数。
输出描述
对于每次询问,在一行上连续的输出一个整数,表示不同的选座方案数,由于答案可能很大,请将答案对 998 244 353 取模后输出。
样例1
输入
1 3 4
0 0 0
0 1 2 3
输出
0 3 4 6
说明
对于第三次询问,有以下几种选座方案:
选择第 1 行,第一个朋友坐第 1 列,第二个朋友坐第 2 列;
选择第 1 行,第一个朋友坐第 2 列,第二个朋友坐第 1 列;
选择第 1 行,第一个朋友坐第 2 列,第二个朋友坐第 3 列;
选择第 1 行,第一个朋友坐第 3 列,第二个朋友坐第 2 列。
样例2
输入
2 4 3
1 1 0 1
0 0 0 0
1 3 8
输出
1 0 0
样例3
输入
4 4 2
1 0 0 0
0 0 0 0
0 0 0 1
1 1 0 1
1 2
输出
4 4