【题解】AcWing 第71场周赛题解

A. 三个整数

题目链接:AcWing 4621. 三个整数

一眼题,由于保证了一定有解,所以只需要保证 x , y x,y x,y 尽量大, z z z 尽量小即可。即 x x x b b b y y y c c c z z z c c c

#include <iostream>
int a, b, c;
int main() {
    scanf("%d%d%d", &a, &b, &c);
    printf("%d %d %d", b, c, c);
    return 0;
}
B. 整数拆分

题目链接:AcWing 4622. 整数拆分

我们知道,素数的定义就是因数只有 1 1 1 和它本身。所以,我们要将 n n n 尽量拆分成由若干个素数之和。

根据哥德巴赫猜想,每一个不小于 6 6 6 的偶数都是两个奇素数之和,每一个不小于 9 9 9 的奇数都是三个奇素数之和。所以,当 n n n 不是素数时,最优解一定为两个或三个的素数之和。

// 我的代码
#include <iostream>
const int N = 1e6 + 9;
int pc, rP[N];
bool isP[N];
void Eratos() {
    for (int i = 2; i < N; i++)
        isP[i] = true;
    for (long long i = 2; i < N; i++) {
        if (isP[i]) {
            rP[++pc] = i;
            for (long long j = i * i; j < N; j += i)
                isP[j] = false;
        }
    }
}
bool isPrime(long long x) {
    for (long long i = 1; 1ll*rP[i]*rP[i]<=x and i<=pc; i++)
        if (x % rP[i] == 0)
            return false;
    return true;
}
int main() {
    Eratos();
    int n;
    scanf("%d", &n);
    if (isPrime(n)) puts("1");
    else {
        for (int i = 1; rP[i]<=n/2 and i<=pc; i++) {
            if (isPrime(n - rP[i])) {
                puts("2");
                return 0;
            }
        }
        puts("3");
    }
    return 0;
}

(听了 y 总讲解才发现这里都不用筛素数)

C. 买糖果

题目链接:AcWing 4623. 买糖果

当时在这道题上卡了好久,听了 y 总讲解后才知道直接暴力就能过

没有什么技术含量的暴力题~~(我居然没过)~~,直接看代码注释吧。

#include <iostream>
const int N = 2e5 + 9;
int n, a[N];
long long T, res;
int main() {
    scanf("%d%lld", &n, &T);
    for (int i = 1; i <= n; i++)
        scanf("%d", a + i);
    while (true) {
        long long sum = 0, cnt = 0;
        // sum 累加这一轮买糖果所需要花费的钱
        // cnt 统计这一轮能买多少颗糖果
        for (int i = 1; i <= n; i++)
            if (sum + a[i] <= T) { // 如果当前这一轮还能买得起这家糖果店的糖,那就累加起来
                sum += a[i];
                cnt++;
            }
        if (!cnt) break; // 已经买不起任何糖果了,直接退出循环
        res += T / sum * cnt;
        // T / sum: 这一轮能重复多少次
        // 比如说,你有 114514 元钱,糖果的价钱依次是 1 1 4 5 1 4
        // 则与第一轮的购买方案相同的情况有 114514 / (1 + 1 + 4 + 5 + 1 + 4) 种
        // 所以,总共就可以购买 114514 / (1 + 1 + 4 + 5 + 1 + 4) * 6 颗糖果
        T %= sum;
        // 重复完若干轮后,你还有多少钱
    }
    printf("%lld", res);
    return 0;
}

这里需要再补充一下时间复杂度的问题。

我一开始也想到了这样暴力,但是我认为这样一定会 TLE(看上去的时间复杂度在 O ( N 2 ) \mathcal{O}(N^2) O(N2) 级别,会超时),所以我没有这么写。但是,通过仔细分析就可以发现,当我们每一轮执行完 T %= sum 后, T T T 一定会至少折半。

这里我们分两种情况讨论:第一种是 T 2 < s u m ≤ T \frac{T}{2} < sum\leq T 2T<sumT,第二种是 s u m ≤ T 2 sum\leq \frac{T}{2} sum2T

如果是第一种,那么模完后的结果为 T - sum,结果一定小于 T 2 \frac{T}{2} 2T

如果是第二种,那么模完后的结果也显然小于 T 2 \frac{T}{2} 2T

所以,这里的时间复杂度其实是 O ( n   l o g   T ) \mathcal{O}(n\ log\ T) O(n log T) 的,最大计算次数为 2 × 1 0 5 × 60 = 1.2 × 1 0 7 2\times 10^5\times 60 = 1.2\times 10^7 2×105×60=1.2×107,可以通过。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值