今天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]]
标签:differ,int,sum,mid,include,CSP,模拟,指针 From: https://www.cnblogs.com/jueqingfeng/p/17588869.html