【CF比赛记录】Codeforces Round 1013 (Div. 3)

比赛链接

本文发布于博客园,会跟随补题进度实时更新,若您在其他平台阅读到此文,请前往博客园获取更好的阅读体验。

跳转链接:https://www.cnblogs.com/TianTianChaoFangDe/p/18796248

开题情况

打回蓝了。

很基础的一场,不过怎么有人会因为没取模白挂两发啊啊啊!太羞耻了!

A. Olympiad Date

很简单,判断一下日期里面的数字什么时候全部数量充足就行了。
点击查看代码

cpp 复制代码
//20250103

void solve()
{
    int n;std::cin >> n;
    std::vector<int> a(n);
    std::vector<int> cnt(10);

    for(auto &i : a) {
        std::cin >> i;
    }

    auto check = [&]() -> bool {
        if(cnt[0] < 3)return false;
        if(cnt[1] < 1)return false;
        if(cnt[2] < 2)return false;
        if(cnt[3] < 1)return false;
        if(cnt[5] < 1)return false;

        return true;
    };

    for(int i = 0;i < n;i ++) {
        cnt[a[i]] ++;

        if(check()) {
            std::cout << i + 1 << '\n';
            return;
        }
    }

    std::cout << 0 << '\n';
}

B. Team Training

首先很明显从大到小排序,然后双指针搜索就行了。
点击查看代码

cpp 复制代码
void solve()
{
    int n;std::cin >> n;
    i64 x;std::cin >> x;
    std::vector<i64> a(n + 1);

    for(int i = 1;i <= n;i ++)std::cin >> a[i];

    std::sort(a.begin() + 1, a.end(), std::greater());

    int sum = 0;
    for(int i = 1, j = 1;i <= n;i = j + 1) {
        j = i;
        while(j + 1 <= n && a[j] * (j - i + 1) < x)j ++;

        if(a[j] * (j - i + 1) >= x) {
            sum ++;
        }
    }

    std::cout << sum << '\n';
}

C. Combination Lock

说实话这题做了有点久,因为验证结论的时候算错了一步导致误以为结论是错的。

根据样例猜的,偶数一定无解,奇数的话,把题目样例都变成 \(1\) 开头,很明显就是先奇数后偶数就行。
点击查看代码

cpp 复制代码
#include <bits/stdc++.h>
#define inf32 1e9
#define inf64 2e18
#define ls o << 1
#define rs o << 1 | 1

using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned int;

const int N = 2e5 + 9;

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

    if(n % 2 == 0) {
        std::cout << -1 << '\n';
        return;
    }

    for(int i = 1;i <= n;i += 2) {
        std::cout << i << ' ';
    }

    for(int i = 2;i <= n;i += 2) {
        std::cout << i << ' ';
    }

    std::cout << '\n';
}

D. Place of the Olympiad

凳子越长肯定可以坐越多人,所以答案具有单调性,因此可以二分。

在二分的 check 中,尽可能只留一个格子作为过道,使能坐的人尽可能多,看能否坐下所有人。
点击查看代码

cpp 复制代码
void solve()
{
    i64 n, m, k;std::cin >> n >> m >> k;

    i64 l = 0, r = m + 1;

    auto check = [&](i64 x) -> bool {
        i64 ch = m % (x + 1);
        i64 sum = m / (x + 1) * x * n;

        sum += ch * n;
        return sum >= k;
    };

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

    std::cout << r << '\n';
}

E. Interesting Ratio

根据唯一分解定理,我们可以把两个数 \(x\) 和 \(y\) 分解成一系列素数的素数的幂次的乘积,并且有:两个数的最大公因数,就是对每个素数的指数取小的那个乘起来,两个数的最小公倍数,就是对每个素数的指数取大的那个乘起来。

因此,如果满足两个数的最小公倍数和最大公因数的商是一个素数,这两个数应该有且仅有一个素数的指数不同,并且差值为 \(1\)。

那么这题的思路就很明确了,对于每一个素数,看他在范围内能乘多少个数,就是这个素数对应的答案。

对所有素数的答案求和即为总的答案。
点击查看代码

cpp 复制代码
#include <bits/stdc++.h>
#define inf32 1e9
#define inf64 2e18
#define int long long
#define ls o << 1
#define rs o << 1 | 1

using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned int;

const int N = 1e7 + 9;
int prime[N];
bool notprime[N];
int tot = 0;

void init() {
    notprime[0] = notprime[1] = true;

    for(int i = 2;i <= 10'000'000;i ++) {
        if(!notprime[i])prime[++ tot] = i;

        for(int j = 1;j <= tot && i * prime[j] <= 10'000'000;j ++) {
            notprime[i * prime[j]] = true;
            if(i % prime[j] == 0) {
                break;
            }
        }
    } 
}

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

    i64 ans = 0;
    for(int i = 1;i <= tot && prime[i] <= n;i ++) {
        int a = 1;
        int b = prime[i];
        ans += n / b;
    }

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

signed main()
{
    std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);

    init();

    int t = 1;std::cin >> t;
    while(t --)solve();

    return 0;
}

F. Igor and Mountain

没错,就是这个题,赛时没看到输出那里有个 998244353,挂了一发,血压起来了。

这个题是一个很简单的 DP,因为题目限制得很明确:

  • 只能一层一层爬。
  • 每一层最多使用两个可抓点。

那么思路就很明晰了,首先从低一层往当前层进行状态转移,然后再在同层进行状态转移。

由于手臂距离为 \(d\),所以低一层的第 \(j\) 个点位向当前层转移的范围应该是 \([j - \sqrt{d ^ 2 - 1}, j - \sqrt{d ^ 2 - 1}]\),由于相邻两个平方数之间的差值至少都是 \(1\),因此 \(\sqrt{d ^ 2 - 1} = d - 1\),避免浮点数精度误差。

同层转移也是一样的,对于当前层第 \(j\) 个点的转移范围应该是 \([j - d, j + d]\)。

但是如果暴力转移,复杂度为 \(O(nm^2)\),因此要考虑优化:我们发现,每一个状态向周围的转移都是一个区间,那么我们就可以使用差分来优化区间修改,时间复杂度优化为 \(O(nm)\),足以通过此题。
点击查看代码(省略了取模类)

复制代码
#include <bits/stdc++.h>
#define inf32 1e9
#define inf64 2e18
#define int long long
#define ls o << 1
#define rs o << 1 | 1

using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned int;

void solve()
{
    int n, m, d;std::cin >> n >> m >> d;
    std::vector<std::vector<char>> a(n + 1, std::vector<char>(m + 1));
    int x = d - 1;

    for(int i = 1;i <= n;i ++) {
        for(int j = 1;j <= m;j ++) {
            std::cin >> a[i][j];
        }
    }

    std::vector<std::vector<Z>> dp(n + 1, std::vector<Z>(m + 1, 0));

    std::vector<Z> f(m + 2, 0);
    for(int i = n;i >= 1;i --) {
        for(auto &i : f)i = 0;
        if(i == n) {
            for(int j = 1;j <= m;j ++) {
                if(a[n][j] == 'X')dp[n][j] = 1;
                else dp[n][j] = 0;
            }
        } else {
            for(int j = 1;j <= m;j ++) {
                int d = x;
                if(a[i + 1][j] == 'X') {
                    int l = std::max(j - d, 1ll);
                    int r = std::min(j + d, m);
                    f[l] += dp[i + 1][j];
                    f[r + 1] -= dp[i + 1][j];
                }
            }
            for(int j = 1;j <= m;j ++) {
                f[j] += f[j - 1];
                if(a[i][j] == 'X') {
                    dp[i][j] += f[j];
                }
            }
        }

        for(auto &i : f)i = 0;
    
        for(int j = 1;j <= m;j ++) {
            if(a[i][j] == 'X') {
                int l = std::max(j - d, 1ll);
                int r = std::min(j + d, m);
                f[l] += dp[i][j];
                f[j] -= dp[i][j];
                f[j + 1] += dp[i][j];
                f[r + 1] -= dp[i][j];
            }
        }
        
        for(int j = 1;j <= m;j ++) {
            f[j] += f[j - 1];
            if(a[i][j] == 'X') {
                dp[i][j] += f[j];
            }
        }
    }

    Z sum = 0;
    for(int j = 1;j <= m;j ++) {
        sum += dp[1][j];
    }

    std::cout << sum << '\n';
}
相关推荐
天天超方的4 天前
【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(4)
做题记录
天天超方的18 天前
【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(2)
做题记录
天天超方的19 天前
【题解】CF2077B Finding OR Sum
位运算·交互题·cf
天天超方的21 天前
【CF VP记录】Codeforces Round 1008 (Div. 2)
做题记录
天天超方的25 天前
【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
做题记录
天天超方的1 个月前
Codeforces Round 1007 (Div. 2) 比赛记录
cf·做题记录
_yuen8 个月前
「比赛记录」CF Round 954 (Div. 3)
cf·比赛记录
越1210 个月前
C. Rooks Defenders(树状数组)
开发语言·数据结构·c++·算法·cf
sluckystar1 年前
题解:CF1951E(No Palindromes)
c++·cf