1. 比赛报告
共8题, 前3题AC, 第4题TLE, 5-7题AC, 第8题没做
本篇题解只提供前7道题
2. 题解
2.1 A. Morning
2.1.1 题目大意:
你需要输入
t
t
t组四位数密码, 输入设备是1234567890
, 每次光标都在
1
1
1上, 你可以输入这个数字或将光标移动到相邻的数字, 问输入密码所需的最少秒数
2.1.2 题目解析:
用字符串存数, 每次要移动的距离是 a b s ( s i − s i − 1 ) abs(s_i - s_{i-1}) abs(si−si−1), 0 0 0排在最后一位, 所以遇到 0 0 0时可以赋值为 10 10 10, 也就是 s i = ′ 0 ′ + 10 s_i = '0' + 10 si=′0′+10
2.1.3 AC代码:
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
int t, x, a, b, c, d;
int main(){
scanf("%d", &t);
while(t--){
// 我用int类型做, 但string会更方便
scanf("%d", &x);
a = x / 1000;
b = x / 100 % 10;
c = x / 10 % 10;
d = x % 10;
if(a == 0) a = 10;
if(b == 0) b = 10;
if(c == 0) c = 10;
if(d == 0) d = 10;
printf("%d\n", a-1 + abs(a-b) + abs(b-c) + abs(c-d) + 4);
}
return 0;
}
2.2 B. Chemistry
2.2.1 题目大意:
输入长度为 n n n的字符串 s s s, 问从中删除 k k k个字符并重新排列是否能形成回文字符串
2.2.2 题目解析:
如果一个字符串是回文的, 那么它里面每个字符 出现的次数是奇数 的个数小于等于 1 1 1, 每一个奇数次数的字符都可以删掉一个达到对称
可以记录每个字符出现的次数 c i c_i ci, 统计 c i c_i ci中奇数的个数 c n t cnt cnt, 如果 c n t − k < = 1 cnt - k <= 1 cnt−k<=1输出 Y E S YES YES, 否则输出 N O NO NO
2.2.3 AC代码:
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
int t, n, k, c[30], l, cnt;
string s;
int main(){
scanf("%d", &t);
while(t--){
cnt = 0;
memset(c, 0, sizeof(c));
scanf("%d %d", &n, &k);
cin >> s;
l = s.size();
for(int i = 0;i < l;i++)
c[s[i]-'a']++;
for(int i = 0;i < 26;i++)
if(c[i] & 1) cnt++;
if(cnt - k <= 1) printf("YES\n");1
else printf("NO\n");
}
return 0;
}
2.3 C. Raspberries
2.3.1 题目大意:
输入 n n n个整数组成的数组 a a a和一个数字 2 ≤ k ≤ 5 2 \le k \le 5 2≤k≤5, 可以每次使一个数字 + 1 +1 +1, 问最少的操作次数使 ( ∏ i = 1 n a i ) ∣ k (\displaystyle \prod_{i=1}^{n}{a_i}) \mid k (i=1∏nai)∣k
2.3.2 题目解析:
当 k k k为质数时, 可以把其中一个a_i 增加到 增加到 增加到k 的倍数 , 也就是求 的倍数, 也就是求 的倍数,也就是求\min(k - a_i % k)$
当 k = 4 k = 4 k=4时, 4 = 2 × 2 4 = 2 \times 2 4=2×2, 所以只要有两个数都是 2 2 2的倍数就能使乘积达到 4 4 4
可以用 o d d odd odd记录偶数个数, e v e n even even记录奇数个数, 如果 o d d ≥ 2 odd \ge 2 odd≥2答案是 0 0 0, 再如果 o d d = 1 odd = 1 odd=1且 e v e n ≥ 1 even \ge 1 even≥1答案是 1 1 1, 因为把一个奇数 + 1 +1 +1就可以有 2 2 2个偶数, 再如果 e v e n ≥ 2 even \ge 2 even≥2答案是 2 2 2, 因为把两个奇数 + 1 +1 +1就可以得到 2 2 2个偶数
2.3.3 AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int T, n, k;
int v[10]; // 可以按照这个代码也可以根据注释全都修改
int main(){
scanf("%d", &T);
while(T--){
memset(v, 0, sizeof(v));
scanf("%d %d", &n, &k);
int t;
while(n--){
scanf("%d", &t);
v[t % k]++; // 这里存v[k - t % k]也可以
}
if(v[0]) printf("0\n"); // 这里就要改成v[k]
else if(k == 4){
if(v[2] >= 2) printf("0\n"); // 要按照解析中的使用odd和even
else if(v[3] >= 1) printf("1\n");
else if(v[2] == 1 && v[1] >= 1) printf("1\n");
else if(v[2] == 0 && v[1] >= 2) printf("2\n");
}
else for(int i = k;i >= 1;i--) // 正序, if不用改, 输出i
if(v[i]){
printf("%d\n", k - i);
break;
}
}
return 0;
}
2.4 D. In Love
2.4.1 题目大意:
有 q q q次操作, 每次会加入或移除一个区间 ( l , r ) (l, r) (l,r), 每次操作回答: 是否有任何两个区间没有重合部分
2.4.2 当时思路:
定义一个差分数组 d i f dif dif, 每次添加区间时 d i f l + 1 , d i f r + 1 − 1 dif_l + 1, dif_{r+1} - 1 difl+1,difr+1−1, 反之移除区间, 用变量 c n t cnt cnt记录到现在有多少个区间
用树状数组统计前缀和
a
a
a, 如果有一个
a
i
=
c
n
t
a_i = cnt
ai=cnt说明所有区间都在这一点重合 输出NO
, 否则至少有两个区间没有重合部分输出YES
最后试验过树状数组会TLE, 线段树AC
2.4.3 题目解析:
查找是否有区间不重合, 可以转化为是否有任意一个 min ( r ) < m a x ( l ) \min(r) < max(l) min(r)<max(l), 可以使用自动排序的容器, 如priority_queue, multiset不去重排序, 移除时优先队列无法快速找到元素, multiset有find函数可以使用
2.4.4 AC代码:
#include <iostream>
#include <set>
#include <cstring>
using namespace std;
int q;
multiset<int> L, R;
int main(){
scanf("%d", &q);
char op[10];
int l, r;
while(q--){
scanf("%s", op);
scanf("%d %d", &l, &r);
if(op[0] == '+'){
L.insert(l);
R.insert(r);
}
else{
L.erase(L.find(l));
R.erase(R.find(r));
}
if(L.size() >= 1 && *R.begin() < *L.rbegin())
printf("YES\n");
else printf("NO\n");
}
return 0;
}
2.5 E. Look Back
2.5.1 题目大意:
输入长度为 n n n的数组 a a a, 可以让 a i ∗ = 2 a_i\; *\!\!= \;2 ai∗=2, 问最少几次操作可以是 a a a变成单调不递减序列
2.5.2 题目解析:
如果直接模拟最后一位最大可以达到 1 0 9 × 2 1 0 5 ≈ 1 0 31002 10^9 \times 2^{10^5} \approx 10^{31002} 109×2105≈1031002, 所以可以用 t i t_i ti表示 a i = a i × 2 t i a_i = a_i \times 2^{t_i} ai=ai×2ti, 也就是要使 a i × 2 t i ≥ a i − 1 × 2 t i − 1 a_i \times 2^{t_i} \ge a_{i-1} \times 2^{t_{i-1}} ai×2ti≥ai−1×2ti−1
我们比较 a i a_i ai与 a i − 1 a_{i-1} ai−1, 分三步走:
- a i = a i − 1 a_i = a_{i-1} ai=ai−1: 保持 a i a_i ai不变即可, 所以 t i = t i − 1 t_i = t_{i-1} ti=ti−1
- a i > a i − 1 a_i > a_{i-1} ai>ai−1: 为了更划算要降低 t i t_i ti, 这时 a i a_i ai与 a i − 1 a_{i-1} ai−1差了几倍的 2 2 2, t i t_i ti就可以减几
- a i < a i − 1 a_i < a_{i-1} ai<ai−1: 为达成目标要升高 t i t_i ti, 这时 a i a_i ai与 a i − 1 a_{i-1} ai−1差了几倍的 2 2 2, t i t_i ti就可以加几
2.5.3 AC代码:
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
ll T, n, a[N], t[N];
int main(){
scanf("%lld", &T);
while(T--){
scanf("%lld", &n);
ll ans = 0;
for(int i = 1;i <= n;i++)
scanf("%lld", &a[i]);
for(int i = 2;i <= n;i++){
if(a[i] >= a[i-1])
t[i] = max(0ll, (ll)(t[i-1] - floor(log2(1.0 * a[i] / a[i-1]))));
if(a[i] < a[i-1])
t[i] = max(0ll, (ll)(t[i-1] + ceil(log2(1.0 * a[i-1] / a[i]))));
ans += t[i];
}
printf("%lld\n", ans);
}
return 0;
}
2.6 F. You Are So Beautiful
2.6.1 题目大意:
输入长度为 n n n的数组 a a a, 求子**数组(连续)的个数, 满足当此子数组做为子序列(不连续)**出现时只能有一个, 也就是只有一种方法能选出此子数组
例如[1,5,6,1,7,2], 如果要选后3个作为一个答案会发现前面还有一个 1 1 1, 前面的 1 1 1加上后面的 7 , 2 7, 2 7,2作为子序列出现时就会重复
2.6.2 题目解析:
就像给出的例子, 一个数组要可以选择必须左端点的左边没有相同的数, 右端点同理
可以先正序预处理: 每个点 i i i前面有多少点是第一个出现的, 存入数组 c c c
再倒叙如果 a i a_i ai是从右往左数第一个数 a n s = a n s + c i ans = ans + c_i ans=ans+ci
2.6.3 AC代码:
#include <iostream>
#include <cstdio>
#include <map>
using namespace std;
const int N = 1e5 + 5;
int t, n, a[N], c[N];
map<int, bool> m;
int main(){
scanf("%d", &t);
while(t--){
m.clear();
long long ans = 0;
scanf("%d", &n);
for(int i = 1;i <= n;i++){
scanf("%d", &a[i]);
c[i] = c[i-1];
if(!m[a[i]]) c[i]++, m[a[i]] = 1;
}
m.clear();
for(int i = n;i >= 1;i--)
if(!m[a[i]]) ans += c[i], m[a[i]] = 1;
printf("%lld\n", ans);
}
return 0;
}
2.7 G1. Dances(Easy version)
2.7.1 题目大意:
有两个长度为 n n n的数组 a , b a, b a,b, a 1 = 1 a_1 = 1 a1=1, 每次操作可以从每个数组任意删除一个元素, 问每个 a i < b i a_i < b_i ai<bi需要的最小操作数
2.7.2 题目解析:
如果要删除 a a a中最小的元素, 就一定要删除 b b b中最小的元素, 可以先进行排序, 按照前面所说用双指针做
2.7.3 AC代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5;
int t, n, m, a[N], b[N];
int main(){
scanf("%d", &t);
while(t--){
int ans = 0;
scanf("%d %d", &n, &m);
a[1] = 1;
for(int i = 2;i <= n;i++)
scanf("%d", &a[i]);
for(int i = 1;i <= n;i++)
scanf("%d", &b[i]);
sort(a+1, a+n+1);
sort(b+1, b+n+1);
for(int i = 1, j = 1;i <= n-ans && j <= n;i++, j++)
while(a[i] >= b[j] && j <= n)
j++, ans++;
printf("%d\n", ans);
}
return 0;
}
3. 反思
思路总是僵住, 没有对题目有好的解题思路, 平日没有给同学讲题的机会, 要经常复习且多做题
Y1.10 Dec.2.2023 Sat.