2/20笔记

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]:

  1. 找到区间内所有 x 的位置

    • id = lower_bound(mp[x].begin(), mp[x].end(), l): 第一个大于等于l 的位置。

    • id2 = upper_bound(mp[x].begin(), mp[x].end(), r) - 1: 最后一个小于等于 r 的位置。

  2. 遍历每个 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();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值