AtCoder Beginner Contest 414 题解 A - E

本文为AtCoder Beginner Contest 414 A - E的详细题解

目录

题目A:

题目大意:

解题思路:

代码(C++):

题目B:

题目大意:

解题思路:

代码(C++):

题目C:

题目大意:

解题思路:

代码(C++):

题目D:

题目大意:

解题思路:

代码(C++):

题目E:

题目大意:

解题思路:

代码(C++):

Python写法:

题目A:

题目B:

题目C:

题目D:

题目E:


题目A:

A - Streamer Takahashi

题目大意:

一个主播从L点播到R点,现在有n个观众,可以在xi到yi的时间内观看直播

现在问这n个观众中有多少个观众可以完整的看完整个直播

解题思路:

对于一个观众来说,要想看完完整的直播

也就是说,能观看直播的时间[xi, yi]是要包含主播的开播时间区间[L, R]

也就是要满足xi <= L && R <= yi。

代码(C++):

void solve() {
    int N, L, R;
    std::cin >> N >> L >> R;

    int ans = 0;
    for (int i = 0; i < N; i++) {
        int X, Y;
        std::cin >> X >> Y;

        if (X <= L && R <= Y) {
            ans++;
        }
    }

    std::cout << ans << "\n";
}

题目B:

B - String Too Long

题目大意:

现在有n对字符和整数,分别为(C1, L1),(C2, L2),(C3, L3)...(CN, LN),

现在有一个空字符串S,你需要依次在这个空字符串上面加L1个C1字符,L2个C2字符,L3个C3字符...LN个CN字符。

现在你需要最后的字符串S。

但是如果S的长度大于100,就输出字符串“Too Long”

解题思路:

根据题目大意进行模拟即可,也就是创建一个空字符串,然后每次加上Li个C1字符。

注意的点是,由于长度大于100就输出字符串"Too Long",并且L的取值范围很大

我们可以在进行操作的时候,如果L的大小大于100的话,就直接输出"Too Long"然后退出即可

代码(C++):

void solve() {
    int N;
    std::cin >> N;

    std::vector<std::pair<char, int>> a(N);
    for (int i = 0; i < N; i++) {
        char c;
        int l;
        std::cin >> c >> l;
        a.push_back({c, l});
    }

    std::string s = "";
    for (auto& [c, l] : a) {
        if (l > 100) {
            std::cout << "Too Long\n";
            return;
        }
        std::string cur(l, c);
        s += cur;
        if (s.size() > 100) {
            std::cout << "Too Long\n";
            return;
        }
    }

    std::cout << s << "\n";
}

题目C:

C - Palindromic in Both Bases

题目大意:

现在给你一个整数N,你需要找出从1到N这N个整数中,满足十进制和A进制表示都是回文数

N的最大值为1e12

解题思路:

题目条件:数字满足十进制和A进制都表示的是回文数。

这题首先想到的枚举思路可能是:从1枚举到N然后判断回文数,然后再判断题目条件

对于判断回文数和判断题目条件操作的时间复杂度都可以视为O(1),也就是总复杂度是O(N)

但是N的取值是1e12,肯定超时

那么我们开始优化,我们可以直接枚举1 到 N里面的回文数(十进制)。

可以用如下的枚举方法,由于是回文数字,我们只需要枚举到N的一半就行了,另外一半直接根据前一半生成就行了。

N最大为1e12,也就是我们枚举到1e6就行了。

也就是现在的时间复杂度为O(N / 2)

具体的实现可以看代码。

代码(C++):

void solve() {
    int A;
    i64 N;
    std::cin >> A >> N;

    std::vector<i64> pow10(8, 1);
    for (int i = 1; i <= 7; i++) {
        pow10[i] = pow10[i - 1] * 10;
    }

    i64 ans = 0;

    auto build = [&](i64 l, bool odd) -> i64 {
        i64 x = l;
        if (odd) {
            l /= 10;
        }
        while (l > 0) {
            x = x * 10 + l % 10;
            l /= 10;
        }
        return x;
    };

    auto check2 = [&](i64 x, int A) -> bool {
        std::vector<int> d(64);
        int n = 0;
        while (x > 0) {
            d[n] = x % A;
            n++;
            x /= A;
        }
        if (n == 0) {
            d[n++] = 0;
        }
        for (int i = 0; i < n / 2; i++) {
            if (d[i] != d[n - i - 1]) {
                return false;
            }
        }
        return true;
    };

    for (int len = 1; len <= 7; len++) {
        i64 L = pow10[len - 1];
        i64 R = pow10[len] - 1;
        for (i64 h = L; h <= R; h++) {
            i64 p1 = build(h, 1);
            if (p1 > N) {
                std::cout << ans << "\n";
                return;
            }
            if (p1 <= N && check2(p1, A)) {
                ans += p1;
            }

            i64 p2 = build(h, 0);
            if (p2 <= N && check2(p2, A)) {
                ans += p2;
            }
        }
    }

    std::cout << ans << "\n";
}

题目D:

D - Transmission Mission

题目大意:

现在有一条坐标轴,坐标轴上面有房子,现在有N个房子,第i个房子的坐标为Xi。

现在你需要在坐标轴上面放基站,一个强度信号为x的基站,可以覆盖方圆x / 2的房子。

现在你需要在坐标轴上放M个基站(你可以放在任意位置),并且给每个基站设定一个信号强度,

现在你的目标是,在保证N个房子都被基站覆盖的情况下,让总的基站强度最小。

输出这个最小值。

解题思路:

我们观察题目条件:一个强度信号为x的基站,可以覆盖方圆x / 2的房子。

我们可以转换成,一个信号强度为x的基站,可以看作一条长度为x的线段,基站在线段中点,线段内的房屋都可以被覆盖。

我们既然要覆盖这X个房子,我们先对这X个房子的坐标进行排序,好进行后面的处理。

现在这X个房子,我们可以看成X - 1条线段。

现在我们的目标是,有M条线段,我们要给这M条线段设置长度,让其覆盖N个点,并且保证M条线段的长度总和尽可能的小。

我们可以这么做,先假设M条线段的长度总和为X[N - 1] - X[0]。

由于我们只需要覆盖点,那么我们可以从中剔除两个房子所连成的线段,可以证明最多只能剔除

N -M个。

我们只需要剔除长度最大的那N - M 个线段即可。

具体实现可以看代码

代码(C++):

void solve() {
    int N, M;
    std::cin >> N >> M;

    std::vector<i64> X(N);
    for (int i = 0; i < N; i++) {
        std::cin >> X[i];
    }

    std::ranges::sort(X);

    std::vector<i64> diff(N - 1);
    for (int i = 0; i < N - 1; i++) {
        diff[i] = X[i + 1] - X[i];
    }

    std::ranges::sort(diff);

    i64 ans = 0;
    for (int i = 0; i < N - M; i++) {
        ans += diff[i];
    }

    std::cout << ans;
}

题目E:

E - Count A%B=C

题目大意:

现在你需要找出这样一个数对(a, b, c)的数量,两两不相同,并且都在1 到 N 内。

满足a % b == c。

输出满足这样的数对的数量

解题思路:

我们可以发现,我们可以这么求,(a, b)数对的数目为N * (N - 1) / 2。

我们只需要减去里面不满足的数目即可,不满足的数目为枚举i从1到N,N / i的总和。

但是N的大小为1e12,这样做还是会超时。

我们可以用整除分块进行优化

具体实现可以参考代码(这里我参考星子哥写法Submission #67528319 - Mirrativ Programming Contest 2025 (AtCoder Beginner Contest 414)

代码(C++):

using i64 = long long;
const int mod = 998244353;
const int inv2 = (mod + 1) / 2; // 2的逆元

void solve() {
    i64 n;
    std::cin >> n;
    
    i64 ans;
    // 计算 N(N+1)/2 mod 998244353
    ans = n % mod * ((n + 1) % mod) % mod * inv2 % mod;
    
    // 整除分块计算
    i64 i = 1;
    while (i <= n) {
        i64 d = n / i;
        i64 nxt = n / d;
        
        // 计算当前块贡献
        i64 len = (nxt - i + 1) % mod;
        i64 term = (d % mod) * len % mod;
        
        // 减去块内贡献
        ans = (ans - term + mod) % mod;
        
        i = nxt + 1;  //跳到下个块起始位置
    }
    
    std::cout << ans << '\n';
}

Python写法:

题目A:

def solve():
    N, L, R = MII()
    ans = 0
    for _ in range(N):
        X, Y = MII()
        if X <= L and R <= Y:
            ans += 1
    print(ans)

题目B:

def solve():
    n = II()
    a = []
    for _ in range(n):
        c, l = input().split()
        a.append([c, int(l)])
    
    s = ""
    for c, l in a:
        if l > 100 or len(s) > 100:
            print("Too Long")
            return
        s += c * l
    
    print(s if len(s) <= 100 else "Too Long")

题目C:


def solve():
    A = II()
    N = II()
    pow10 = [10**i for i in range(8)]

    def build(num: int, odd: bool) -> int:
        x = num
        if odd:
            num //= 10
        while num > 0:
            x = x * 10 + num % 10
            num //= 10
        return x
    
    def check(num: int) -> bool:
        d = []
        while num > 0:
            d.append(num % A)
            num //= A
        n = len(d)
        for i in range(n // 2):
            if d[i] != d[n - 1 - i]:
                return False
        return True

    ans = 0
    for i in range(1, 8):
        L = pow10[i - 1]
        R = pow10[i]
        for num in range(L, R):
            num1 = build(num, 1)
            if num1 > N:
                print(ans)
                return
            if check(num1):
                ans += num1
            num2 = build(num, 0)
            if num2 <= N and check(num2):
                ans += num2
    
    print(ans)

题目D:

def solve():
    N, M = MII()
    X = LII()

    X.sort()

    diff = [0] * (N - 1)
    for i in range(N - 1):
        diff[i] = X[i + 1] - X[i]
    diff.sort()

    ans = 0
    for i in range(N - M):
        ans += diff[i]
    print(ans)

题目E:


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值