Martian148's blog Martian148's blog
首页
  • ICPC 算法笔记
  • ICPC 算法题解
  • 体系结构
  • 高等数学
  • 线性代数
  • 概率论与数理统计
  • 具体数学
  • Martian148的奇思妙想
  • 游记
  • 通识课笔记
关于
  • useful 网站
  • 友情链接
  • 分类
  • 归档

Martian148

一只热爱文科的理科生
首页
  • ICPC 算法笔记
  • ICPC 算法题解
  • 体系结构
  • 高等数学
  • 线性代数
  • 概率论与数理统计
  • 具体数学
  • Martian148的奇思妙想
  • 游记
  • 通识课笔记
关于
  • useful 网站
  • 友情链接
  • 分类
  • 归档
  • codeforses 题解

  • atcoder 题解

  • XCPC 题解

  • 校训练赛题解

    • 【2024暑#108】ACM暑期第三次测验(个人赛)题解
      • A - 猫抓老鼠
        • Solution
        • Code
      • B - 字符变换
        • Solution
        • Code
      • C - 7777777
        • Solution
        • Code
      • D - 武术大师
        • Solution
        • Code
      • E - 扫雷
        • Solution
        • Code
      • F - 整除(middle)
        • Solution
        • Code
    • 【2024暑#109】ACM暑期第四次测验(个人赛)题解
    • 【2024暑#110】ACM暑期第五次测验(个人赛)题解
    • 【2025秋120】ACM周测(个人赛,div3)题解
    • Sakura Tears training 4 题解
    • Sakura Tears training 5 题解
  • 牛客题解

  • 蓝桥杯题解

  • 典题选讲

  • 杂题

  • 算法题解
  • 校训练赛题解
martian148
2024-08-03
目录

【2024暑#108】ACM暑期第三次测验(个人赛)题解

# A - 猫抓老鼠

# Solution

经典的逆序对问题,这里就不过多阐述了

有递归和树状数组两种写法,自行百度即可

# Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
    freopen ("A.in", "r", stdin);
    int n; cin >> n;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    
    ll ans = 0;

    auto cdq = [&] (auto cdq, int L, int R) {
        if (L == R) return;
        int mid = (L + R) / 2;
        cdq(cdq, L, mid); cdq(cdq, mid + 1, R);
        int l = L, r = mid + 1;
        vector<int> tmp;
        while (l <= mid || r <= R) {
            if (r > R || (l <= mid && a[l] <= a[r])) {
                tmp.push_back(a[l]); l++;
            } else {
                tmp.push_back(a[r]); r++;
                ans += mid - l + 1;
            }
        }
        for (int i = L; i <= R; i++) a[i] = tmp[i - L];
    };

    cdq(cdq, 1, n);
    cout << ans << endl;
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# B - 字符变换

# Solution

查看 (S[i]−T[i])%26(S[i]-T[i])\%26(S[i]−T[i])%26 是否相同即可

# Code

#include <bits/stdc++.h>
using namespace std;
int main() {
    string S, T; cin >> S >> T;
    set<int> st;
    for (int i = 0; i < S.size(); i++) {
        int cz = (S[i] - T[i] + 26) % 26;
        st.insert(cz);
    }
    cout << (st.size() == 1 ? "Yes" : "No") << endl;
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12

# C - 7777777

# Solution

首先,如果我们删除了一些 7,如果仍然存在从原点看不到的 7,则我们也可以删除这些 7,而不会改变从原点看到的 7 的数量。

因此,问题可以重新表述如下:

给定 NNN 个 7,你可以删除其中一些,以便所有剩余的 7 都可以从原点看到。找到剩余 7 的最大数量。

此外,我们可以把每个 7 视为一个开区间,区间的两端对应于其两个端点的极角,这样问题就可以重新表述如下:

给定 NNN 个开放区间。第 iii 个区间的左端点为 f(xi,yi−1)f(x_i,y_i-1)f(xi​,yi​−1),右端点为 f(xi−1,yi)f(x_i-1,y_i)f(xi​−1,yi​),其中 f(i,j)f(i,j)f(i,j) 是一个由两个实数 (i,j)(i,j)(i,j) 表示坐标 (i,j)(i,j)(i,j) 极角的函数。

最多可以选择多少个区间,使得任意两个区间不重叠?

这就是著名的区间调度问题。可以通过以下贪心算法解决,同样可以应用于这个问题。

重复以下操作。

  1. 让 Rmax⁡R_{\max}Rmax​ 成为已选择区间中(右端点的)最大坐标。如果存在尚未选择的区间,其左端点坐标大于或等于 Rmax⁡R_{\max}Rmax​,则选择其中右端点最小的区间。
  2. 否则,如果不存在左端点坐标大于等于 Rmax⁡R_{\max}Rmax​ 的区间,则终止过程。

在实现时,将 NNN 个区间按其右端点坐标的递增顺序排序。然后维护 Rmax⁡R_{\max}Rmax​,如果某个区间的左端点大于等于 RmaxR_{max}Rmax​,那么直接把 Rmax⁡R_{\max}Rmax​ 更新成当前区间的右端点

时间复杂度为 O(Nlog⁡N)O(N \log N)O(NlogN)。

# Code

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

struct Frac {
    ll p, q; // p / q
    Frac(ll p = 0, ll q = 1) : p(p), q(q) {}
    bool operator < (const Frac &f) const { return p * f.q < q * f.p; }
    bool operator <= (const Frac &f) const { return p * f.q <= q * f.p; }
};

int main() {
    int N; cin >> N;
    vector<ll> x(N), y(N);
    for (int i = 0; i < N; i++) cin >> x[i] >> y[i];
    vector<pair<Frac, Frac>> que(N);
    for (int i = 0; i < N; i++) que[i] = make_pair(Frac(y[i], x[i] - 1), Frac(y[i] - 1, x[i]));
    sort(que.begin(), que.end());
    int cnt = 0;
    Frac R_max = Frac(0, 1);
    for (auto cur : que) {
        if (R_max <= cur.second) { // cur.second 是左端点
            cnt += 1;
            R_max = cur.first; // 更新右端点
        }
    }
    cout << cnt << endl;
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# D - 武术大师

# Solution

可以使用记忆化 DFS,当然,由于满足 Ai,j<iA_{i,j} < iAi,j​<i 说明需要在 iii 之前学的招式都是在 iii 之前出现过的,所以我们从后往前,记录一个数组 used[i]used[i]used[i] 表示 iii 是否需要学习,如果 used[i]=1used[i]=1used[i]=1 表示需要学习,那么把 iii 之前需要学的招式 jjj 都标记为 111

注意,这里的 Ai,jA_{i,j}Ai,j​ 最好使用 vectorvectorvector 来存储

# Code

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    int n; cin >> n;
    vector<vector<int>> p(n, vector<int>());
    vector<int> T(n), K(n);
    for (int i = 0; i < n; i++) {
        cin >> T[i] >> K[i];
        for (int j = 0; j < K[i]; j++) {
            int x; cin >> x;
            p[i].push_back(x);
        }
    }
    vector<int> used(n, 0); used[n - 1] = 1;
    for (int i = n - 1; i >= 0; i--) {
        if (used[i]) {
            for (int j = 0; j < K[i]; j++) {
                used[p[i][j] - 1] = 1;
            }
        }
    }
    long long ans = 0;
    for (int i = 0; i < n; i++) 
        ans += T[i] * used[i];
    cout << ans << endl;
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# E - 扫雷

# Solution

因为 NNN 很小,直接暴力 dfs 就可以了

但是正解是使用 DP 来做的

定义 F[i]F[i]F[i] 表示以第 iii 节点结束的最大值,则

f[i]=max⁡f[j]+a[i](g[i][j]=1) f[i]=\max{f[j]}+a[i](g[i][j]=1) f[i]=maxf[j]+a[i](g[i][j]=1)

对于输出,使用 pre[i]pre[i]pre[i] 记录 iii 的前驱节点,然后递归输出即可

# Code

#include <bits/stdc++.h>
using namespace std;

int main() {
    freopen ("E.in", "r", stdin);
    int n; cin >> n;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    vector<vector<int>> g(n + 1, vector<int>(n + 1, 0));
    vector<int> f(n + 1, 0), pre(n + 1, 0);
    for (int i = 1; i <= n; i++) {
        for (int j = i + 1; j <= n; j++) {
            int x; cin >> x;
            g[i][j] = x;
        }
    }
    int ans = -1, ans_i;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j < i; j++) {
            if (g[j][i] == 1 && f[j] > f[i]) {
                f[i] = f[j];
                pre[i] = j;
            }
        }
        f[i] += a[i];
        if (f[i] > ans) {
            ans = f[i];
            ans_i = i;
        }
    }

    auto print = [&](auto && print, int x) -> void {
        if (pre[x] == 0) {
            cout << x << " ";
            return;
        }
        print(print, pre[x]);
        cout << x << " ";
    };

    print(print, ans_i);
    cout << endl;
    cout << ans << endl;
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

# F - 整除(middle)

# Solution

还是容斥原理

定义 A={能被2整除},B={能被3整除},C={能被7整除},U=全集A=\{\text{能被2整除}\},B=\{\text{能被3整除}\},C=\{能被7整除\},U=\text{全集}A={能被2整除},B={能被3整除},C={能被7整除},U=全集

那么所求的就是 (U−A)(U−B)C=U2C−UAC−UBC+UABC=C−AC−BC+ABC(U-A)(U-B)C=U^2C-UAC-UBC+UABC=C-AC-BC+ABC(U−A)(U−B)C=U2C−UAC−UBC+UABC=C−AC−BC+ABC

写成式子的形式就是:

n7−n2×7−n3×7+n2×3×7 \frac{n}{7}-\frac{n}{2\times 7}-\frac{n}{3\times 7} +\frac{n}{2\times 3\times 7} 7n​−2×7n​−3×7n​+2×3×7n​

# Code

#include<bits/stdc++.h>
using namespace std;
int main() {  
    long long n;  
    cin>>n;
    long long sum= (n/7)-(n/14)-(n/21)+(n/42);  
    cout<<sum<<endl;  
    return 0;  
}
1
2
3
4
5
6
7
8
9
上次更新: 2025/04/08, 18:03:31
CCPC2024 哈尔滨站 题解
【2024暑#109】ACM暑期第四次测验(个人赛)题解

← CCPC2024 哈尔滨站 题解 【2024暑#109】ACM暑期第四次测验(个人赛)题解→

最近更新
01
Java基础语法
05-26
02
开发环境配置
05-26
03
pink 老师 JavaScript 学习笔记
05-26
更多文章>
Theme by Vdoing | Copyright © 2024-2025 Martian148 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式