1 上午的六道题
值得注意的就两道
1.1 模拟抢票,分两档,遍历求组合数的题
#include <iostream>
#include <vector>
using namespace std;
const int MOD = 1e9 + 7;
const int N = 3005;
// 初始化组合数和排列数
vector<vector<long long>> C(N, vector<long long>(N, 0)); // C(n, m) 表示组合数
vector<vector<long long>> A(N, vector<long long>(N, 0)); // A(n, m) 表示排列数
// 使用杨辉三角来计算组合数 C(n, m) 和排列数 A(n, m)
void init() {
for (int i = 0; i < N; i++) {
C[i][0] = 1; // C(n, 0) = 1
A[i][0] = 1; // A(n, 0) = 1
for (int j = 1; j <= i; j++) {
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD;
A[i][j] = (A[i][j - 1] * (i - j + 1)) % MOD;
}
}
}
// 主程序
int main() {
init(); // 初始化杨辉三角
int n, m, q, t;
cin >> n >> m >> q >> t; // 输入 n, m, q, t
vector<int> d(q);
for (int i = 0; i < q; i++) {
cin >> d[i]; // 输入 q 个玩家的登陆天数
}
int n1 = 0, n2 = 0;
for (int x : d) {
if (x <= t) {
n1++;
} else {
n2++;
}
}
long long ans = 0;
if (n > t) { // 只能抢第二档位的票
for (int i = 0; i <= n1; i++) { // 遍历 <= t 的玩家分配到第一档位的票数量
for (int j = 0; j <= n2; j++) { // 遍历 > t 的玩家分配到第二档位的票数量
int now = max(i - m, 0) + j; // 当前第一档票被抢完后,第二档位的需求量
if (now < m) { // 如果第二档票还足够,则统计这种排列方式
ans = (ans + C[n1][i] * C[n2][j] %MOD * A[i + j][i + j] % MOD * A[q - i - j][q - i - j] % MOD) % MOD;
}
}
}
} else { // 可以抢第一档位的票
for (int i = 0; i <= n1; i++) {
for (int j = 0; j <= n2; j++) {
int now = i + min(j, m); // 计算所需的总票数
if (now < 2 * m) { // 如果总票数足够
ans = (ans + C[n1][i] * C[n2][j] % MOD * A[i + j][i + j] % MOD * A[q - i - j][q - i - j] % MOD) % MOD;
}
}
}
}
cout << ans << endl; // 输出结果
return 0;
}
1.2 考dfs并查集,每个格子两种类型,求某个种类2的连通块变成种类1后与周围的种类1联通的最大值
#include <iostream>
#include <vector>
#include <unordered_set>
using namespace std;
int n, m;
vector<string> s;
vector<int> pre, cnt;
vector<vector<bool>> vis;
unordered_set<int> sett;
int find(int x) {
if (x == pre[x]) return x;
return pre[x] = find(pre[x]); // 路径压缩
}
void dfs(int x, int y, int z) {
if (x < 0 || x >= n || y < 0 || y >= m || vis[x][y] || s[x][y] == '.') return;
int tz = find(x * m + y); // 找到该格子的根
if (tz != z) {
pre[tz] = z; // 合并两个区域
cnt[z] += cnt[tz]; // 更新区域大小
}
vis[x][y] = true; // 标记已访问
dfs(x + 1, y, z); // 向四个方向递归
dfs(x - 1, y, z);
dfs(x, y + 1, z);
dfs(x, y - 1, z);
}
int dfs2(int x, int y) {
if (x < 0 || x >= n || y < 0 || y >= m) return 0;
if (s[x][y] == '#') {
int z = find(x * m + y); // 找到该格子的根
if (sett.find(z) != sett.end()) return 0; // 如果已处理过该连通块则跳过
sett.insert(z); // 添加到集合中
return cnt[z]; // 返回该连通块的大小
}
if (vis[x][y]) return 0; // 如果已访问则跳过
vis[x][y] = true; // 标记已访问
int ret = 1; // 计数当前空地
ret += dfs2(x + 1, y); // 向四个方向递归
ret += dfs2(x - 1, y);
ret += dfs2(x, y + 1);
ret += dfs2(x, y - 1);
return ret; // 返回总空地大小
}
int main() {
cin >> n >> m; // 读取n和m
s.resize(n);
for (int i = 0; i < n; ++i) {
cin >> s[i]; // 读取每行字符串
}
pre.resize(n * m);
cnt.resize(n * m);
vis.resize(n, vector<bool>(m, false));
for (int i = 0; i < n * m; ++i) {
pre[i] = i; // 初始化并查集
cnt[i] = 1; // 初始化每个区域的大小
}
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (!vis[i][j] && s[i][j] == '#') {
dfs(i, j, i * m + j); // 扫描每个连通块
}
}
}
int ans = 0;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (!vis[i][j] && s[i][j] == '.') {
sett.clear();
ans = max(ans, dfs2(i, j)); // 寻找最大的空地
}
}
}
cout << ans << endl; // 输出结果
return 0;
}
2 第三组
2.1 求某个区间内包含x的子区间数量
1. 预处理存储所有值的位置
-
使用
map<int, vector<int>> mp
存储每个值在数组中的所有位置。 -
例如,若数组为
[1, 2, 3, 2, 1]
,则mp[2] = [2, 4]
,表示值2
出现在位置2
和4
。
2. 查询处理
对于每个查询 [l,r,x]:
-
找到区间内所有 x 的位置:
-
id = lower_bound(mp[x].begin(), mp[x].end(), l)
: 第一个大于等于l 的位置。 -
id2 = upper_bound(mp[x].begin(), mp[x].end(), r) - 1
: 最后一个小于等于 r 的位置。
-
-
遍历每个 x 的位置:
-
对于每个位置 p=mp[x][i],计算以 p 为最左边的 x 的子区间数量。
-
3. 子区间数量计算
-
第一个 x 的位置:
-
起点范围:从 l 到 p。
-
终点范围:从 p 到 r。
-
子区间数量:(p−l+1)×(r−p+1)
-
-
含义:所有起点在 [l,p],终点在[p,r] 的区间
-
-
后续的 x 的位置:
-
起点范围:从前一个 x 的位置 prev 的下一个位置到当前 x 的位置 p。
-
终点范围:从 p 到 r。
-
子区间数量:
(p−prev)×(r−p+1) -
含义:所有起点在 (prev,p],终点在[p,r] 的区间
-
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
int t;
void solve() {
int n;
cin >> n;
vector<int> a(n + 1);
map<int, vector<int>> mp;
for (int i = 1; i <= n; i++) {
cin >> a[i];
mp[a[i]].push_back(i);
}
int q;
cin >> q;
for (int i = 0; i < q; i++) {
int l, r, x;
cin >> l >> r >> x;
int id = lower_bound(mp[x].begin(), mp[x].end(), l) - mp[x].begin();
int id2 = upper_bound(mp[x].begin(), mp[x].end(), r) - mp[x].begin() - 1;
ll ans = 0;
if (id < mp[x].size()) {
for (int i = id; i <= id2; i++) {
if (i == id) {
ans = ans + 1ll * (mp[x][i] - l + 1) * (r - mp[x][i] + 1);
} else {
ans = ans + 1ll * (mp[x][i] - mp[x][i - 1]) * (r - mp[x][i] + 1);
}
}
}
cout << ans << "\n";
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
solve();
}