CSP模拟-8

发布时间 2023-07-28 20:44:33作者: Joker__King

今天T1终于看懂辣。。。。但今天名次最低QAQ。T1没算空间复杂度,直接炸QAQ

T1 Coprime 2

今天T1确实简单,将输入的数的质数公因数用埃氏筛筛出来,用一个数组存下来。每次将质因数的倍数用 \(flag\) 存下 \(true\) ,表示这个数存在因数与输入的数重复的情况,让后就没有辣。

正确性的话:一个合数因数一定会被分解成一个或多个质因数,所以每次找质因数就行辣。(我相信你们都明白吧QAQ)

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <vector> 

using namespace std;

int n, a[5211314], m;
int gcd_sum, ans[2000010], sum, is_gcd[2000010], rec_gcd[2000010];
bool barrel[2000010], is_prime[2000010], flag[2000010];

void Prime() {
	//正经的埃氏筛
	memset(is_prime, true, sizeof(is_prime));
	for (int i = 2; i <= m; ++ i) {
		if (is_prime[i] == false) continue;
		for (int j = i; j <= m; j = j + i) {
			is_prime[j] = false;
			//找输入的数的质因数
			if (barrel[j] == true && rec_gcd[i] == false) {
				//如果i的倍数是输入的数并且质因数i没有被存过
				is_gcd[++ gcd_sum] = i;
				rec_gcd[i] = true;
				//这里记录的原因是保证复杂度
			}
		}
	}
	return;
}

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++ i) {
		scanf("%d", &a[i]);
		barrel[a[i]] = true;
		//将输入的数用桶先存下来
	}
	Prime();
	for (int i = 1; i <= gcd_sum; ++ i) {
		for (int j = is_gcd[i]; j <= m; j += is_gcd[i]) {
			//将每个质数因数的倍数标记
			flag[j] = true;
		}
	}
	for (int i = 1; i <= m; ++ i) {
		if (flag[i] == false) {
			//不存在输入的数的质因数
			ans[++ sum] = i;
		}
	}
	//输出
	printf("%d\n", sum);
	for (int i = 1; i <= sum; ++ i) {
		printf("%d\n", ans[i]);
	}
	return 0;
}

T2 Dist Max 2

又是逆天二分QAQ,永远写不对的边界条件,我真服了。

看见找最小值的最大值,一眼丁真鉴定为二分答案。

设我们现在已经枚举到 \(mid\),所以我们这时必定会存在 \(i, j\) 满足 \(\left| x_i - x_j \right| \geq mis\) 并且满足 \(\left| y_i - y_j \right| \geq mid\) 。

此时我们直接遇事不决先排序,将点以 \(x\) 为关键字进行升序排序,然后维护双指针就行了。

我们的双指针 \(l,r\) 表示左区间 \(1 \sim l\) 以 \(l\) 为结尾,右区间 \(r \sim n\) 以 \(r\) 为右端点。这时我们要保证区间 \(1 \sim l\) 的每一个点的横坐标与区间 \(r \sim n\) 的每一个点的横坐标的差大于我们二分到的 \(mid\)。将这两个符合差为 \(mid\) 要求的区间内点的纵坐标 \(Y\) 的最大值与最小值分别与另一区间内纵坐标 \(Y\) 的最小值最大值作差,判断是否大于等于 \(mid\)。若大于,则符合题目要求,返回真。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;

int n;

struct Picture {
	int x, y;
	bool operator < (const Picture & b) const {
		if (x != b.x) return x < b.x;
		return y < b.y;
	}//sort的时候用 
}a[1000100];

bool check(int differ) {
	int MaxY = -1e9, MinY = 1e9;
	//在l之前Y轴的最值 
	for (int r = 1, l = 0; r <= n; ++ r) { 
		//这里面的l表示左半部分的右端点,r表示右半部分的左端点
		//l就是左指针,r就是右指针 
		while (l < r && a[r].x - a[l + 1].x >= differ) {
			//若存在更小的X轴之差符合差最小值得differ的条件,就更新l的值 
			MaxY = max(MaxY, a[l + 1].y);
			MinY = min(MinY, a[l + 1].y);
			//将左指针新包含的范围内的MaxY和MinY更新 
			l ++;
		}
		if (MaxY - a[r].y >= differ) return true;
		if (a[r].y - MinY >= differ) return true;
		//判断左边的Y的最值与右侧的Y的最值的差是不是大于differ 
		//在循环里我们相当于将右指针后面的数全部枚举了一遍最大值,最小值 
		//左指针除了l=r=1的时候处理右指针,其他时候都是左指针跟着右指针移动
		//所以在右指针更新的时候,左指针左边的MaxY与MinY也都被更新了,正确性可以保证 
	}
	return false;
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; ++ i) {
		scanf("%d%d", &a[i].x, &a[i].y);
	}
	sort(a + 1, a + 1 + n);
	int l = 0, r = 1e9, ans = 0;
	while (l <= r) {
		//二分找最值 
		int mid = (l + r) >> 1;
		if (check(mid)) {
			l = mid + 1;
			ans = mid;
		}
		else r = mid - 1;
	}
	printf("%d\n", ans);
	return 0;
}

PS:顺带附上找最最大的最小值

while (l <= r) {
	int mid = (l + r) >> 1;
	if (check(mid)) {
		r = mid - 1;
		ans = mid;
	}
	else l = mid + 1;
}

T3 [ABC221H] Count Multiset

这边建议[这篇题解](「解题报告」[ABC221H] Count Multiset - K8He - 洛谷博客 (luogu.com.cn))。写的很好,复杂度优。

#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <cstdio>

#define mod 998244353

using namespace std;
typedef long long ll;

int n, m, f[5210][5210], g[5210][5210], sum[5210][5210];

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++ i) {
		if (i <= m) 
			f[i][0] = 1;
		for (int j = 1; j <= n; ++ j) {
			if (j >= i) 
				f[i][j] = g[i][j] = f[i][j - i];
			sum[i][j] = (sum[i - 1][j] + g[i][j]) % mod;
			f[i][j] = ((f[i][j] + sum[i - 1][j]) % mod - sum[max(0, i - m - 1)][j] + mod) % mod;
		}
	}
	for (int i = 1; i <= n; ++ i) {
		cout << g[i][n] << endl;
	}
	return 0;
}

T4 Julia the snail

数据加强版,吉司机线段树不会,咕。

总结

本次考试分数最高,但名词最低QAQ。T1直接炸,没考虑空间复杂度,下一次不能再犯辣。T2没看见是整数,不敢开 \(long long\),开了 \(longdouble\) 输出浮点,又炸。![[23FDBE81.jpg]]