用集合统计被占用的不同行列,再用容斥计算这些整行整列覆盖的格子数。
OJ: luogu
题目 ID: P3913
难度:入门
标签:集合容斥原理计数python
日期: 2026-07-16 19:20
题意
在 n*n 棋盘上放若干车,统计至少被一辆车所在行或所在列覆盖的格子数。
思路
只需知道有多少个不同的被攻击行 R 和列 C。所有行覆盖 R*n 个格子,所有列覆盖 C*n 个格子,行列交叉的 R*C 个格子被重复计算一次,因此答案是:
车的位置可能重复行或重复列,分别用集合去重。
Python 知识
set.add自动忽略重复行列编号。len(rows)、len(columns)就是容斥公式所需的集合大小。- 逐行读取百万个车,避免一次性 token 列表带来的峰值内存。
- Python 大整数可直接容纳
n<=1e9的棋盘计数。 /home/rainboy/mycode/hugo-blog/content/program_language/python/collections_toolkit.md:集合成员去重。/home/rainboy/mycode/hugo-blog/content/program_language/python/oj_input_output_cheatsheet.md:大规模逐行输入。
代码
python
import sys
def main():
read = sys.stdin.buffer.readline
n, rook_count = map(int, read().split())
rows = set()
columns = set()
for _ in range(rook_count):
row, column = map(int, read().split())
rows.add(row)
columns.add(column)
attacked_rows = len(rows)
attacked_columns = len(columns)
print(n * attacked_rows + n * attacked_columns - attacked_rows * attacked_columns)
if __name__ == "__main__":
main()cpp
/**
* P3913 车的攻击
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-27 00:00
* update_at: 2026-07-27 00:00
*/
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1000005;
int rows[MAXN], cols[MAXN];
int n, k;
int main() {
scanf("%d%d", &n, &k);
for (int i = 1; i <= k; ++i) {
scanf("%d%d", &rows[i], &cols[i]);
}
sort(rows + 1, rows + k + 1);
sort(cols + 1, cols + k + 1);
// 去重后统计不同行数和列数
int row_cnt = unique(rows + 1, rows + k + 1) - (rows + 1);
int col_cnt = unique(cols + 1, cols + k + 1) - (cols + 1);
// 被攻击格子数 = 行 * n + 列 * n - 行 * 列
long long ans = 1LL * row_cnt * n + 1LL * col_cnt * n - 1LL * row_cnt * col_cnt;
printf("%lld\n", ans);
return 0;
}复杂度
期望时间复杂度
总结
棋盘虽然巨大,但覆盖只由行集合和列集合决定;集合去重加两集合容斥即可。