Codeforces Round 1006 (Div. 3)

A. cntew World, cntew Me, cntew Array

题目大意

给你一个长为n的全0数组,每个位置可以修改为不超过p的绝对值的数字,要求和为k,求最小操作次数,不可能输出-1

解题思路

显然前面尽可能的拉满,然后看最后需不需要额外补一个就行

代码实现

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    int tt;
    std::cin >> tt;

    while (tt--) {
        int n, k, p;
        std::cin >> n >> k >> p;

        k = std::abs(k);
        if (n * p < k) {
            std::cout << -1 << "\n";
        } else {
            std::cout << (k + p - 1) / p << "\n";
        }
    }
}

B. Having Been a Treasurer in the Past, I Help Goblins Deceive

题目大意

给你一个字符串只包含 "-" 和 "_",让你重排字符串,问最多能有多少子序列是 "-_-"

解题思路

显然把"_"放中间,"-"平均放两边是最多的

代码实现

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    int tt;
    std::cin >> tt;

    while (tt--) {
        i64 n, cnt = 0;
        std::cin >> n;

        std::string s;
        std::cin >> s;

        for (auto ch : s) {
            cnt += ch == '_';
        }

        std::cout << cnt * ((n - cnt) / 2 * ((n - cnt + 1) / 2)) << "\n";
    }
}

C. Creating Keys for StOansrages Has Become My Main Skill

题目大意

给定n和x,要求用不超过n个数字使得它们按位或等于x,同时要让这些数字的mex尽可能大

解题思路

枚举mex,如果当前mex不会让x产生变化则一直枚举下去

代码实现

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    int tt;
    std::cin >> tt;

    while (tt--) {
        int n, x, mex = 0, cnt = 0;
        std::cin >> n >> x;

        while (mex < n && ((mex & x) == mex)) {
            cnt |= mex;
            mex++;
        }

        if (mex == n && cnt != x) {
            mex--;
        }

        for (int i = 0; i < n; i++) {
            std::cout << (i < mex ? i : x) << " \n"[i == n - 1];
        }
    }
}

D. For Wizards, the Exam Is Easy, but I Couldn't Handle It

题目大意

允许操作一次,把位于l的数字放到r,求让逆序对最少的任意一组lr

解题思路

n的范围很小,因此暴力枚举每一个i换到右边的每一个位置的时候,让顺序逆序倒置的数量差求个最大值即可

代码实现

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    int tt;
    std::cin >> tt;

    while (tt--) {
        int n;
        std::cin >> n;

        std::vector<int> a(n);
        for (int i = 0; i < n; i++) {
            std::cin >> a[i];
        }

        int l = 0, r = 0, cnt = 0;
        for (int i = 0; i < n; i++) {
            int cnt1 = 0, cnt2 = 0;
            for (int j = i + 1; j < n; j++) {
                cnt1 += a[i] > a[j];
                cnt2 += a[i] < a[j];
                if (cnt < cnt1 - cnt2) {
                    l = i, r = j;
                    cnt = cnt1 - cnt2;
                }
            }
        }

        std::cout << l + 1 << " " << r + 1 << "\n";
    }
}

E. Do You Love Your Hero and His Two-Hit Multi-Target Attacks?

题目大意

构造不超过五百个点对,使他们之间欧氏距离等于曼哈顿距离的点对数量为k

解题思路

首先计算全在一条线上的数量,发现远超最大的要求,而最小值显然就是0,因此考虑会有多条平行线段,发现他们对目标值的贡献大小是可控的,所以答案就在其中,每次新开一条不重叠的线段一直算贡献直到小于k的最大值,重复直到k为0即可

代码实现

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    int tt;
    std::cin >> tt;

    while (tt--) {
        int k, x = 0, y = 0, cnt = 0;
        std::cin >> k;
        
        std::vector<std::array<int, 2>> ans;
        while (cnt < k) {
            std::vector<std::array<int, 2>> xy;
            for (int i = 0; y < k; i++) {
                if (cnt + i > k) {
                    break;
                }
                xy.push_back({x, y});
                ans.push_back({x, y});
                cnt += i;
                x++;
            }
            y++;
        }

        std::cout << ans.size() << "\n";
        for (auto [x, y] : ans) {
            std::cout << x << " " << y << "\n";
        }
    }
}

F. Goodbye, Banker Life

题目大意

类似杨辉三角,其中把加法换成异或,输出第n行

解题思路

显然输出的元素不是0就是k,打表发现组合数为偶数的输出0,奇数的输出k,于是可以转换为对组合数%2(此处的组合数是 \(C_{n-1}^{m-1}\) ),用卢卡斯定理即可求得答案(当mod=2时有一个结论, \(C_n^m \% 2\) 等价于 \(n\&m==m\)

代码实现

// 做法1
#include <bits/stdc++.h>

using i64 = long long;

i64 ksm(i64 a, i64 n, i64 mod) {
    i64 res = 1;
    a = (a % mod + mod) % mod;
    while (n) {
        if (n & 1) {
            res = (a * res) % mod;
        }
        a = (a * a) % mod;
        n >>= 1;
    }
    return res;
}

int C(int n, int m, int mod) {
    if (m > n) {
        return 0;
    }
    int res = 1;
    for (int i = 1, j = n; i <= m; i++, j--) {
        res = (i64)res * j % mod;
        res = (i64)res * ksm(i, mod - 2, mod) % mod;
    }
    return res;
}

int lucas(i64 a, i64 b, i64 p) {
    if (a < p && b < p) {
        return C(a, b, p);
    }
    return (i64)C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    int tt;
    std::cin >> tt;
    
    while (tt--) {
        int n, k;
        std::cin >> n >> k;

        for (int i = 0; i < n; i++) {
            std::cout << lucas(n - 1, i, 2) * k << " \n"[i == n - 1];
        }
    }
}
// 做法2
#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    int tt;
    std::cin >> tt;

    while (tt--) {
        int n, k;
        std::cin >> n >> k;

        for (int i = 0; i < n; i++) {
            std::cout << ((n - 1 & i) == i) * k << " \n"[i == n - 1];
        }
    }
}

G. I've Been Flipping Numbers for 300 Years and Calculated the Sum

题目大意

给你n,k,求2-k进制下的n反转后变回十进制的和,对答案模1e9+7

解题思路

可以发现k>n的部分只有一位, \(\sqrt{n}\)\(n\) 的部分会有两位,观察数据范围可以发现,对于根号的复杂度是可以容忍的,因此把求和分成三部分

  • k > n
  • 2 - \(\sqrt{n}\)
  • \(\sqrt{n}\) - \(n\)

对于这个两位数(\(\lfloor n/i \rfloor\), \(n \% i\)),反转之后是(\(n \% i\)\(\lfloor n/i \rfloor\)),对于这些向下取整相同的部分(设这个区间是lr),他们的余数是一个等差数列(设首项\(a=n\%l\),公差\(d=\lfloor n/i \rfloor\)),可以用分块来解决

\(ans=\sum_{i=l}^r (a-(i-l)*d)*i+d\)
\(ans=(r-l+1)*d+\sum_{i=l}^r (a*i - (i^2 - i*l)*d)\)

\(sum1 = \sum_{i=l}^r i\) \(sum2 = \sum_{i=l}^r i^2\)

\(ans = (r-l+1)*d+sum1*a-(sum2-sum1*l)*d\)
\(ans = (r-l+1)*d+sum1*n-sum2*d\)

代码实现

#include <bits/stdc++.h>

using i64 = long long;
const int MOD = 1e9 + 7;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    int tt;
    std::cin >> tt;

    while (tt--) {
        i64 n, k, ans = 0;
        std::cin >> n >> k;

        if (k > n) {
            ans = ans + (k - n) % MOD * n % MOD;
            k = n;
        }

        for (int i = 2; i <= std::min((i64)std::sqrt(n), k); i++) {
            i64 x = n, res = 0;
            while (x) {
                res = (res * i + x % i) % MOD;
                x /= i;
            }
            ans = (ans + res) % MOD;
        }

        auto sum1 = [&](i64 l, i64 r) -> i64 { return (r * (r + 1) / 2 - l * (l + 1) / 2) % MOD; };
        auto sum2 = [&](i64 l, i64 r) -> i64 { return (r * (r + 1) * (2 * r + 1) / 6 - l * (l + 1) * (2 * l + 1) / 6) % MOD; };

        for (int l = sqrt(n) + 1, r; l <= k; l = r + 1) {
            r = std::min(n / (n / l), k);
            i64 a = n % l, d = n / l;
            ans += (r - l + 1) * d % MOD;
            ans += sum1(l - 1, r) * n % MOD;
            ans -= sum2(l - 1, r) * d % MOD;
        }

        std::cout << (ans % MOD + MOD) % MOD << "\n";
    }
}
posted @ 2025-02-26 23:24  udiandianis  阅读(89)  评论(0)    收藏  举报