CSP-J 2021 题解:
放在一切的前面:
我以前写过CSP-J2021,成了我至今为止阅读量最多的文章,但是有些代码没有达到最优,思路也不是简洁有力,所以说用了这样一篇文章。
by: RyanCh
T1:
第一题是一道简单的数学题:
就是求一个数使这个数 ttt 在 [L,R][L,R][L,R] 中使得 t mod nt \bmod ntmodn 最大。
我们先思考一下 t mod nt \bmod ntmodn 最大是多少?
我们通过一些常识和推理得出 t mod nt \bmod ntmodn 最大为 n−1n - 1n−1 。
所以,我们如果存在这种情况的 ttt 那么我们的答案就是 n−1n - 1n−1。
如果没有呢?
那么答案就是 R mod nR \bmod nRmodn。
代码如下:
#include<bits/stdc++.h>
#define ll long long
ll n, L, R;
int main() {
scanf("%lld%lld%lld", &n, &L, &R);
if(n * (L / n + 1) - 1 <= R) printf("%lld", n - 1);
else printf("%lld", R % n);
return 0;
}
T2:
模拟插入排序。
本题中, 插入排序的特点为稳定的、升序的结果。
稳定就表示相同的数值时,位置在前,最后的结果就在前。
升序表示从小到大。
我们可以将数值和当前的位置分别存储。
O(nlogn)O(n \log n)O(nlogn) 来求出开始时每一个位置数字的排序后位置。
我们只需要在 1 x y 的时候维护数字排序后的位置,就能将 2 x 的时间复杂度降到 O(1)O(1)O(1) 。
因为至多又 500050005000 次操作1,所以上述算法可行。
代码如下:
#include<bits/stdc++.h>
int n, Q;
struct nember{int y, id;}a[8080], b[8080];
bool cmp(nember a, nember b) {
if(a.y != b.y) return a.y < b.y;
return a.id < b.id;
}
int main() {
scanf("%d%d", &n, &Q);
for(int i = 1; i <= n; i++) {
int x;
scanf("%d", &x);
a[i].y = b[i].y = x;
b[i].id = i;
}
std::sort(b + 1, b + n + 1, cmp);
for(int i = 1; i <= n; i++) a[b[i].id].id = i;
while(Q--) {
int opt;
scanf("%d", &opt);
if(opt == 2) {
int x; scanf("%d", &x);
printf("%d\n", a[x].id);
continue;
}
int x, v;
scanf("%d%d", &x, &v);
for(int i = 1; i <= n; i++)
if(i < x) {
if(a[i].y <= v && a[i].y > a[x].y) a[x].id++, a[i].id--;
else if(a[i].y > v && a[i].y <= a[x].y) a[x].id--, a[i].id++;
}
else if(i > x){
if(a[i].y < v && a[i].y >= a[x].y) a[x].id++, a[i].id--;
else if(a[i].y >= v && a[i].y < a[x].y) a[x].id--, a[i].id++;
}
a[x].y = v;
}
return 0;
}
T3:
这道题是一道非常简单朴素的模拟题。
只需要对着这道题的要求来就可以。
题面有3条规则,后两条比较容易完成,第一条要仔细对照。
代码如下:
#include<bits/stdc++.h>
using namespace std;
map<string, pair<int, int> > mp;
int vt[]{255, 255, 255, 255, 65535};
int T;
bool solve(char x) {if('0' <= x && x <= '9') return 1; return 0;}
bool check(string a) {
int cnt = 0, len = a.size(), num = 0;
for(int i = 0; i < len; i++) {
if(solve(a[i])) {
if(num == 0 && a[i] == '0' && solve(a[i + 1]))
return 0;
num = num * 10 + a[i] - '0';
if(num > vt[cnt])
return 0;
}
else {
if(!solve(a[i - 1]) || !solve(a[i + 1])) return 0;
num = 0;
if(cnt == 0 || cnt == 1 || cnt == 2) if(a[i] != '.') return 0;
if(cnt == 3) if(a[i] != ':') return 0;
cnt++;
}
}
if(cnt != 4) return 0;
return 1;
}
int main() {
cin >> T;
for(int i = 1; i <= T; i++) {
string s1, s2;
cin >> s1 >> s2;
if(!check(s2)){printf("ERR\n"); continue;}
if(s1 == "Server") {
if(mp[s2].first != 0) printf("FAIL\n");
else mp[s2].first = 1, mp[s2].second = i, printf("OK\n");
continue;
}
else {
if(mp[s2].first == 0) printf("FAIL\n");
else printf("%d\n", mp[s2].second);
continue;
}
}
return 0;
}
46行,不算多。
我以前的代码需要一百多行,就很离谱
但思路还是很清晰的。
T4:
想对思路就很简单了。
可以用队列模拟。
注意队列的使用:
先从队列中拿出数据,对于数据处理后放回队列。
我尝试过不合并能不能AC,然而不能。
所以我们必须合并。
正常的合并方法为起始点替换为较小且终点替换为较大。
然而我们合并后会有一些段是空缺的,可以用数组存储。
代码如下:
#include<bits/stdc++.h>
struct member {
int opt/*0, 1*/, st, nd, cnt;
member(){cnt = 0;}
};
member v(int a, int b, int c) {member s; s.opt = a; s.st = b; s.nd = c; s.cnt = 0; return s;}
std::deque<member> q;
int n, a[200010], maxn, cnt;
bool pd[200010];
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
for(int i = 1; i <= n; i++) {
int x = i;
while(a[x] == a[i] && x <= n) x++;
q.push_back(v(a[i], i, x - 1));
i = x - 1;
}
while(!q.empty()) {
int last = 2;
while(q.front().cnt == cnt) {
member z = q.front();
q.pop_front();
z.cnt++;
if(last != z.opt){
while(pd[z.st]) z.st++;
printf("%d ", z.st);
pd[z.st] = 1;
z.st++;
last = z.opt;
if(z.st > z.nd) continue;
else q.push_back(z);
}
else {
member t = q.back();
q.pop_back();
if(t.opt != z.opt) q.push_back(t);
else z.st = std::min(t.st, z.st), z.nd = std::max(z.nd, t.nd);
q.push_back(z);
}
}
cnt++;
printf("\n");
}
return 0;
}
7463

被折叠的 条评论
为什么被折叠?



