2024年11月18日
今天是ashang停课的第一天,昨天接到老师的语音通话,得知冲一把有望进省队,下定决心要停一段时间的课。
今天的主要任务是刷历年NOIP的第一题,并尽量看懂第二题的题解。/太菜了/
基础算法练习:二分算法
洛谷P1314 [NOIP2011 提高组] 聪明的质监员:(参考题解完成)
这道题由题意可得需使用二分查找。
验证二分算法在此题的可行性:
时,所有矿石都能选;
时,所有矿石都不能选。符合二分算法。
而对于公式我们可以考虑前缀和,用
求个数的前缀和,用
求检验值的前缀和。
关于边界的考虑,可以设大一点,如
代码如下:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 2e5 + 5;
const int INF = 0x3f3f3f3f;
int v[maxn], w[maxn], l[maxn], r[maxn];
int n, m, s;
int Y;
int pre_n[maxn], pre_v[maxn];
int sum;
void init() {
memset(pre_n, 0, sizeof(pre_n));
memset(pre_v, 0, sizeof(pre_v));
}
bool check(int W) {
init();
Y = 0;
sum = 0;
for (int i = 1; i <= n; i++) {
if (w[i] >= W) {
pre_n[i] = pre_n[i - 1] + 1;
pre_v[i] = pre_v[i - 1] + v[i];
}
else {
pre_n[i] = pre_n[i - 1];
pre_v[i] = pre_v[i - 1];
}
}
for (int i = 1; i <= m; i++) {
Y += (pre_n[r[i]] - pre_n[l[i] - 1]) * (pre_v[r[i]] - pre_v[l[i] - 1]);
}
sum = llabs(Y - s);
if (Y > s) return true;
else return false;
}
signed main() {
// freopen("1314.in", "r", stdin);
// freopen("1314.out", "w", stdout);
int L = INF, R = 0;
scanf("%lld%lld%lld", &n, &m, &s);
for (int i = 1; i <= n; i++) {
scanf("%d%d", &w[i], &v[i]);
L = min(L, w[i]);
R = max(R, w[i]);
}
for (int i = 1; i <= m; i++) {
scanf("%d%d", &l[i], &r[i]);
}
L -= 1, R += 2;
int mid;
int ans = 0x3f3f3f3f3f3f3f3f;
while (L <= R) {
mid = (L + R) >> 1;
if (check(mid)) {
L = mid + 1;
}
else {
R = mid - 1;
}
ans = min(sum, ans);
}
printf("%lld\n", ans);
return 0;
}
注意开了long long后的输入输出处理,以及ans的初始值。
MATH_整数与素数
整除
假设 和
是整数,且
。那么,如果存在一个整数
,使
,则称
整除
,记做
。
如果 且
,称
是
的倍数,
是
的约数。
特别规定:任何整数 都整数 0 ,即
。
整除的性质:
1. 且
2. 若 ,
3.
4. 且
5. 且
对任意整数
和
,
6. 若
7.
8. 且
和
互质
9. 且
和
互质
10. 若
素数
素数(prime number):在大于1的自然数中,除了1和它本身没有其他约数,这样的数称为素数,否则为合数。
素数又称质数,有无限个;
素数 p 的约数只有两个:1 和 p;
0 和 1 既不是素数也不是合数;
基于乘法运算,素数是构成任意 > 1 整数的原子。
素数判定:试除法
若自然数 n 不能被 内的所有数整除,则 n 是素数,时间复杂度:
,适用于
。
性质:大于 4 的素数,总是等于 或
。
bool isPrime(int a) {
if (a < 2) return 0;
for (int i = 2; i <= a / i; i++) {
if (a % i == 0) return 0;
}
return 1;
}
枚举约数
试除法枚举 n 的所有约数
约数都是成对出现的,只需枚举其中较小的一个约数即可(复杂度 )
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e3 + 5;
int divisors[maxn], cntd;
void get_divisors(int x) {
cntd = 0;
for (int i = 1; i <= x / i; i++) {
if (x % i == 0) {
divisors[cntd++] = i;
if (i != x / i) {
divisors[cntd++] = x / i;
}
}
}
sort(divisors, divisors + cntd);
}
int main() {
int n;
scanf("%d", &n);
get_divisors(n);
for (int i = 0; i < cntd; i++) {
printf("%d\n", divisors[i]);
}
return 0;
}
例题练习:CF396B
#include<bits/stdc++.h>
using namespace std;
bool isPrime(int n) {
if (n < 2) return false;
for (int i = 2; i <= n / i; i++) {
if (n % i == 0) return false;
}
return true;
}
long long Gcd(long long x, long long y) {
if (y == 0) return x;
return Gcd(y, x % y);
}
int T;
int main()
{
scanf("%d", &T);
while (T--) {
long long n, v, u;
scanf("%lld", &n);
u = n + 1;
v = n;
while (!isPrime(u)) u++;
while (!isPrime(v)) v--;
long long ans1 = n - u - v + 1, ans2 = (long long)u * v;
ans1 *= 2, ans2 <<= 1, ans1 = ans2 / 2 + ans1;
long long d = Gcd(ans1, ans2);
printf("%lld/%lld\n", ans1 / d, ans2 / d);
}
return 0;
}
2024年11月19日
继续复习数论。
MATH_整数与素数
素数筛
用于求小于等于 n 的所有素数。
暴力:对于 的每个数进行一次素数判定。显然,这种做法的复杂度很高,为
。
可以考虑:对于任一整数 x ,数 x 的所有倍数一定是合数。这样从小到大枚举 x ,可以避免很多非必要的检测。
基本思路:
从小到大枚举 的每个数,把当前这个数的所有倍数标记为合数(筛掉),运行结束时没有被标记的数就是
的所有素数。
素数埃氏筛法:求区间 内的所有素数
· 初始数列:
· 输出最小的素数 ,筛掉 2 的倍数,剩下
· 输出素数 ,筛掉 3 的倍数,剩下
· 输出素数 ,筛掉 5 的倍数,剩下
· 继续以上步骤
埃氏筛法的时间复杂度:
筛选次数:
调和级数: (发散速度非常慢)
素数分布:
P3383 TLE on #5
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int primes[N], cnt;
bool st[N];
void getPrimes(int n) {
for (int i = 2; i <= n; i++) {
if (!st[i]) {
primes[cnt++] = i;
for (int j = 2 * i; j <= n; j += i) {
st[j] = true;
}
}
}
}
int main() {
int n, q, k;
scanf("%d%d", &n, &q);
getPrimes(n);
while (q--) {
scanf("%d", &k);
printf("%d\n", primes[k - 1]);
}
return 0;
}
埃氏筛法缺陷:一个合数会被多次筛去。
素数线性筛法:(欧拉筛)
每个合数仅被其最小质因子筛去,时间复杂度 。
证明 1 :合数仅被其最小质因子筛去
i 循环,扫描 的所有数,令
是要筛去的合数
若 i % primes[j] != 0 ---> primes[j] 不是 i 的质因子,且 primes[j] 必小于 i ---> k 的最小质因子是 primes[j]
若 i % primes[j] == 0 ---> primes[j] 必是 i 的最小质因子 ---> k 的最小质因子是 primes[j]
另外,此时,对于 primes 数组的第 j 素数后的所有素数,i * primes[x] 的最小质因子必是 primes[j] ,为避免重复检测,要跳出对 primes 数组的循环。
证明 2 :≤ n 的所有合数均会被筛去
任一合数 k 均有一个最小质因子 p, 则当 i 枚举到 k / p 时一定会筛掉 k
性质:若 n 为合数,则 n 中至少含有一个 的质因子
应用 1 : 基于 的素数,可筛出 ≤ n 的所有素数。例如,基于 ≤ 100 的 25 个素数,可筛出 ≤ 10000 所有素数。
应用 2 :若不超过 的所有素数均不是 n 的约数,则 n 必为素数。(用于改进的多次判定素数,适用于
)
例:判断一个数是否是素数
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5;
int p[maxn], cnt;
bool st[maxn];
void get_primes(int n) {
for (int i = 2; i <= n; i++) {
if (!st[i]) p[cnt++] = i;
for (int j = 0; p[j] * i <= n; j++) {
st[p[j] * i] = true;
if (i % p[j] == 0) break;
}
}
}
bool is_prime(int x) {
if (x < maxn) return !st[x];
for (int i = 0; i < cnt; i++) {
if (x % p[i] == 0) return false;
if (p[i] > x / p[i]) break;
}
return true;
}
int main() {
get_primes(maxn - 1);
long long s;
scanf("%lld", &s);
printf("%d\n", is_prime(s));
return 0;
}
358

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



