【杂练】CSP-J 2021 题解

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 - 1n1

所以,我们如果存在这种情况的 ttt 那么我们的答案就是 n−1n - 1n1

如果没有呢?

那么答案就是 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(nlog⁡n)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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值