C S P − S 2024 模拟赛 4 补题报告 2024 年 7 月 30 日 − 2023 年 8 月 1 日 b y 邓时飏 CSP \ - \ S \ \ 2024 \ \ 模拟赛4 \ \ 补题报告 \\ 2024年7月30日 - 2023年8月1日 \\ by \ \ \ 邓时飏 CSP − S 2024 模拟赛4 补题报告2024年7月30日−2023年8月1日by 邓时飏
一、做题情况
-
第一题比赛 0 0 0 / 100 100 100 ,赛后通过
-
第二题比赛 0 0 0 / 100 100 100 ,赛后通过
-
第三题比赛 0 0 0 / 100 100 100 ,赛后通过
-
第四题比赛 0 0 0 / 100 100 100 ,赛后通过
-
比赛得分 0 0 0 / 400 400 400 ,赛后补题 400 400 400 / 400 400 400
二、题解报告
T1:
题面:
题目描述
【题目描述】
外星幼儿园的小朋友按照老师的要求玩报数游戏Ⅱ,每个人手里有一张小纸条,上面写着一个数字,每个人都按照顺序报数,报数的方式是这样的 :
每个人听自己的前面一个人报数字的大小,加上自己纸条上的数字求和,并报出自己的数字。
对于第一个人来说,他听到的数字是由老师报出来的。
如果你是老师,请在确保每位同学报的数字是正数的情况下,报出一个最小的正数作为起始值。
【输入格式】
第一行有一个整数 n,输入有多少个同学排队 第二行输入一个数组 nums,数组长度为 n,数字用空格分隔,代表同学按照报数字的顺序排好队以后,每个同学手里小纸条上的数字
【输出格式】
输出一行,共一个数字,输出符合条件的起始值
【样例输入1】
5
-3 2 -3 4 2
【样例输出1】
5
【样例解释1】
在你选择5作为起始值时,五名同学的报数均为正数,为[2,4,1,5,7]
【数据范围】
对于20%的数据来说
1 <= n <= 100
-100 <= nums[i] <= 100
对于100%的数据来说
1 <= n <= 1e5
-1e9 <= nums[i] <= 1e9
做法:
枚举,模拟前缀和
附:AC代码
#include<bits/stdc++.h>
using namespace std;
int n;
long long now, ma, a;
int main() {
cin >> n >> a;
ma = a, now = a;
for (int i = 1; i < n; i++) {
cin >> a;
now = now + a;
ma = min(ma, now);
}
cout << max(1 - ma, (long long)1);
return 0;
}
T2:
题面:
题目描述
题目描述
马克吐温有一本非常著名的小说《百万英镑》,这本小说中主角最后归还了百万英镑给两位富翁。但结果就是两位富翁依然有无穷的问题需要进行社会实验,于是,他们打算进行第二次社会实验。那就是不同财富值的人在一场舞会上会发生什么事情。为了满足自己的好奇,百万富翁们邀请了全伦敦所有人来自己的舞会。舞会开始后他们就后悔了,因为来的人太多了,而且很多人的财富都相同,统计起来太费事了。所以百万富翁们找到你,希望你根据来舞会的时间,找出在一段时间内,来舞会的所有人财富值都互不相同的人数。
输入格式
第一行输入一个n表示有n个人参与舞会。
按照时间顺序输入n个人的财富值。
输出格式
输出在一段时间内参加舞会的所有人财富值都互不相同的人数的最大值。
样例
Input 1
7
2 3 4 5 5 6 7
Output 1
4
数据范围
每个人的财富值不超过100000000000
0 <= n <= 1000000
做法:
要解决这个问题,我们需要找到在一个时间段内参加舞会的所有人的财富值都互不相同的人数的最大值。具体来说,我们要找的是在整个序列中,财富值互不相同的最长子序列的长度。这可以通过使用滑动窗口(双指针)方法来实现。
解题思路
滑动窗口(双指针):
- 我们使用两个指针来维护一个窗口,窗口内的所有财富值都必须是唯一的。
- 使用一个哈希集合(set)来快速检查财富值是否在当前窗口内重复。
- 不断移动右指针来扩展窗口,并根据情况移动左指针来缩小窗口直到没有重复的财富值。
操作步骤:
- 初始化左指针和右指针都指向数组的起始位置。
- 初始化一个哈希集合来存储当前窗口内的财富值。
- 使用右指针遍历所有财富值:
-
- 如果当前财富值不在哈希集合中,添加到集合中,并更新最大长度。
-
- 如果当前财富值已经在哈希集合中,移动左指针缩小窗口,直到当前财富值可以再次被加入哈希集合中。
- 继续这个过程直到右指针遍历完整个数组。
附:AC代码
#include <bits/stdc++.h>
using namespace std;
int n, l, maxx;
vector<long long> w(1000000);
unordered_map<long long, int> c;
int main() {
cin >> n;
if (n == 0) {
cout << 0;
return 0;
}
for (int i = 0; i < n; ++i) cin >> w[i];
for (int i = 0; i < n; ++i) {
c[w[i]]++;
while (c[w[i]] > 1) {
c[w[l]]--;
l++;
}
maxx = max(maxx, i - l + 1);
}
cout << maxx;
return 0;
}
T3:
题面:
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/57abe0e9a3f04157be5bb0ad26a7ee4f.png)
做法:
解题思路
排序和前缀和:
首先将红魔法的法力值进行排序。
计算前缀和数组,用于快速计算某个区间内的红魔法法力值和。
二分查找:
对于每个任务,通过二分查找找到能够满足条件的最小的红魔法种类数。
实现步骤
输入处理:
读取红魔法的数量 n 和任务的数量 m。
读取红魔法的法力值。
读取每个任务的 p 和 q。
排序和前缀和:
将红魔法的法力值排序。
计算前缀和数组。
处理任务:
对于每个任务,使用二分查找找到能够满足条件的最小的红魔法种类数。如果找不到,返回 -1。
附:AC代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, m, cnt, p, q;
int a[500010], sum[500010], sum2[500010];
signed main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; i++) {
sum[i] = sum[i - 1] + a[i];
sum2[i] = sum2[i - 1] + (cnt * a[n - i + 1]);
cnt++;
}
for (int i = 1; i <= m; i++) {
cin >> p >> q;
int l = 1ll, r = min(p, n);
while (l <= r) {
int mid = (l + r) / 2;
if ((sum[n] - sum[n - mid]) * p - sum2[mid] < q) l = mid + 1;
else r = mid - 1;
}
if ((sum[n] - sum[n - min(p, n)]) * p - sum2[min(p, n)] < q) {
cout << -1 << endl;
continue;
}
cout << l << endl;
}
return 0;
}
T4:
题面:
题目描述
有一个长度为n的数列a,a的值在[1,n] 中,现在要统计区间中出现的数都恰好出现2次的区间数。
输入格式
第一行一个整数n.
第二行n个整数表示a.
输出格式
一行一个整数cnt,表示满足条件的区间数.
样例
Input 1
7
5 3 5 5 4 4 3
Output 1
4
样例解释
样例中合法的区间有:[3,4],[5,6],[3,6],[2,7]
数据范围
对于30% 的数据, n≤10^3n≤10
3
.
对于100% 的数据, n≤10^6n≤10
6
.
做法:
附:AC代码
#include <bits/stdc++.h>
using namespace std;
#define L(i, j, k) for (int i = (j); i <= (k); i++)
#define R(i, j, k) for (int i = (j); i >= (k); i--)
#define sz(a) ((int) (a).size())
#define ll long long
#define vi vector<int>
#define ull unsigned long long
const int N = 1 << 20;
int n, a[N];
int lst[N], llst[N];
ull w[N], pre[N], ret;
ll ns;
unordered_map<ull, int> mp;
mt19937_64 orz(time(0) ^ clock());
int main() {
ios :: sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n;
L(i, 1, n) cin >> a[i];
L(i, 1, n) w[i] = orz();
int p = 0;
mp[0] += 1;
L(i, 1, n) {
while (p < llst[a[i]]) mp[pre[p]] -= 1, ++p;
llst[a[i]] = lst[a[i]], lst[a[i]] = i;
pre[i] = pre[i - 1] ^ w[a[i]],ns += mp[pre[i]],mp[pre[i]] += 1;
}
cout << ns << '\n';
return 0;
}
四、赛后总结
提升实力,控制时间。