Educational Codeforces Round 86 个人题解

Educational Codeforces Round 86 个人题解

1342A - Road To Zero(简单分类讨论)

题意:给两个整数 x ​ x​ x y ​ y​ y,需要把这两个数都变成 0 ​ 0​ 0,现在有 a ​ a​ a b ​ b​ b 两个操作,其中 a ​ a​ a 操作花费 a ​ a​ a 的代价将其中一个数字 + 1 ​ +1​ +1 或者 − 1 ​ -1​ 1 b ​ b​ b 操作花费 b ​ b​ b 的代价将两个数字同时 + 1 ​ +1​ +1 或者 − 1 ​ -1​ 1,问最小的代价是多少,需要回答 t ​ t​ t 个案例。

范围 1 ≤ t ≤ 100 , 0 ≤ x , y ≤ 1 e 9 , 1 ≤ a , b ≤ 1 e 9 1 \le t \le 100, 0 \le x, y \le 1e9, 1 \le a, b \le 1e9 1t100,0x,y1e9,1a,b1e9

题解

显然对 x x x y y y 两个数字都进行一次 a a a 操作,其效果等价于进行了一次 b b b 操作,如果 2 ∗ a < b 2*a < b 2a<b b b b 操作完全可以被 a a a 操作取代,否则就需要进行 ∣ x − y ∣ |x-y| xy a a a 操作消除两数的差异再进行 m i n ( x , y ) min(x, y) min(x,y) b b b 操作。

Code

#include <bits/stdc++.h>
#define int long long
#define double long double
using namespace std;

inline int read()
{
    int s = 0, w = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            w = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        s = s * 10 + ch - '0', ch = getchar();
    return s * w;
}

const int MAXN = 2e5;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const double eps = 1e-9;
const double PI = acos(-1.0);

int n;

signed main()
{
    int T = read();
    while (T--)
    {
        int x, y, a, b;
        cin >> x >> y >> a >> b;
        if (x > y)
            swap(x, y);
        if (2 * a <= b)
        {
            cout << a * (x + y) << endl;
        }
        else
        {
            cout << b * x + a * (y - x) << endl;
        }
    }
    return 0;
}

1342B - Binary Period(构造)

题意:给一个 01 ​ 01​ 01 t ​ t​ t,需要找到一个长度不超过 2 ∗ ∣ t ∣ ​ 2*|t|​ 2t 01 ​ 01​ 01 s ​ s​ s 满足: t ​ t​ t s ​ s​ s 的子序列且 s ​ s​ s 拥有最小的循环长度。只需要给出一个满足条件的 s ​ s​ s ,需要回答 t ​ t​ t 个案例。

范围 1 ≤ T ≤ 100 , 1 ≤ ∣ t ∣ ≤ 100 1 \le T \le 100, 1 \le |t| \le 100 1T100,1t100

题解

不要求最优解,考虑找一下规律。可以发现如果 t t t 中只包含 1 1 1 或者只包含 0 0 0,那么 s s s 也只需要包含 1 1 1 或者只包含 0 0 0,循环长度为 1 1 1;如果 t t t 中既有 0 0 0 也有 1 1 1,那么 s s s 可以构造成以 " 01 " "01" "01" 为最小循环子串的长度为 2 ∗ ∣ t ∣ 2*|t| 2t 01 01 01 串,循环长度为 2 2 2,这样对于 t t t 中的每一位都可以在一个 " 01 " ​ "01"​ "01" 循环子串中匹配到。

Code

#include <bits/stdc++.h>
#define int long long
#define double long double
using namespace std;

inline int read()
{
    int s = 0, w = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            w = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        s = s * 10 + ch - '0', ch = getchar();
    return s * w;
}

const int MAXN = 2e5;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const double eps = 1e-9;
const double PI = acos(-1.0);

int n;

signed main()
{
    int T = read();
    while (T--)
    {
        string str;
        cin >> str;
        int cnt1 = 0, cnt2 = 0;
        for (int i = 0; i < str.length(); i++)
        {
            if (str[i] == '0')
                cnt1++;
            else
                cnt2++;
        }
        string ans = "";
        // 只有1
        if (cnt1 == 0)
        {
            for (int i = 0; i < 2 * str.length(); i++)
            {
                ans += '1';
            }
        }
        // 只有0
        else if (cnt2 == 0)
        {
            for (int i = 0; i < 2 * str.length(); i++)
            {
                ans += '0';
            }
        }
        // 两个都有
        else
        {
            for (int i = 0; i < str.length(); i++)
            {
                ans += '0';
                ans += '1';
            }
        }
        cout << ans << endl;
    }
    return 0;
}

1342C - Yet Another Counting Problem(数学+前缀和)

题意:有 t ​ t​ t 个案例,每个案例给两个整数 a ​ a​ a b ​ b​ b,以及 q ​ q​ q 个询问,每个询问给出 l ​ l​ l r ​ r​ r,需要计算在区间 [ l , r ] ​ [l, r]​ [l,r] 中有多少个数字 x ​ x​ x 满足 x % a % b = x % b % a ​ x\%a\%b = x\%b\%a​ x%a%b=x%b%a

范围 1 ≤ t ≤ 100 , 1 ≤ a , b ≤ 200 , 1 ≤ q ≤ 500 , 1 ≤ l ≤ r ≤ 1 e 18 1 \le t \le 100, 1 \le a, b \le 200, 1 \le q \le 500, 1 \le l \le r \le 1e18 1t100,1a,b200,1q500,1lr1e18

题解

可以发现对于给定的 a a a b b b,从小到大满足条件的 x x x 以一个周期进行循环,而这个周期就是 a a a b b b 的公倍数,每个周期内满足条件的 x x x 数量相同。显然 a ∗ b a*b ab a a a b b b 的公倍数,且 a ∗ b ≤ 4 e 4 a*b \le 4e4 ab4e4,那么我们就可以预处理出一个周期内满足条件 x x x 的前缀和,因此就可以对所有的查询区间 [ l , r ] [l, r] [l,r] 进行前缀和差分求解, a n s = p r e [ y ] − p r e [ x − 1 ] ​ ans = pre[y]-pre[x-1]​ ans=pre[y]pre[x1]

Code

#include <bits/stdc++.h>
#define int long long
#define double long double
using namespace std;

inline int read()
{
    int s = 0, w = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            w = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        s = s * 10 + ch - '0', ch = getchar();
    return s * w;
}

const int MAXN = 4e4 + 10;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const double eps = 1e-9;
const double PI = acos(-1.0);

int a, b, q;

int dp[MAXN];

int calc(int x)
{
    // 有x/(a*b)个完整周期,还剩下一个x%(a*b)的周期
    return dp[a * b] * (x / (a * b)) + dp[x % (a * b)];
}

signed main()
{
    int T = read();
    while (T--)
    {
        memset(dp, 0, sizeof(dp));
        cin >> a >> b >> q;
        // 预处理前缀和
        dp[0] = 0;
        for (int i = 1; i <= a * b; i++)
        {
            if (i % a % b == i % b % a)
                dp[i] = dp[i - 1];
            else
                dp[i] = dp[i - 1] + 1;
        }
        for (int i = 0; i < q; i++)
        {
            int x, y;
            cin >> x >> y;
            // 前缀差分
            cout << calc(y) - calc(x - 1) << endl;
        }
    }
    return 0;
}

1342D - Multiple Testcases(二分)

题意:有 n n n 个数字 m 1 ∼ m n m_1\sim m_n m1mn,并且有一种容器,一个容器只能存放 c 1 c_1 c1 ≥ 1 \ge1 1 的数字, c 2 c_2 c2 ≥ 2 \ge2 2 的数字,…, c k c_k ck ≥ k \ge k k 的数字,问至少需要多少个容器才能装下所有数字。

范围 1 ≤ n , k ≤ 2 e 5 , 1 ≤ m i ≤ k , n ≥ c 1 ≥ c 2 ≥ . . . ≥ c k ≥ 1 1\le n, k \le 2e5, 1 \le m_i \le k, n \ge c_1 \ge c_2 \ge ... \ge c_k \ge 1 1n,k2e5,1mik,nc1c2...ck1

题解

考虑到数据范围,我们不能够对每个容器的各个档位去找数字存放,也不能对于每个数字直接遍历去找哪个容器还可以存放,不过可以利用二分进行优化遍历的过程。

首先我们对数字进行排序,从大到小的进行处理,对于每个数字需要找到哪个容器可以容纳下该数字,而进行遍历的效率太低。

基于贪心的想法,一个容器需要尽可能多的存放数字,实在没办法了才使用新的容器,那么最后每个容器中的数字数量会呈现递减的形式,对于这样的一个序列我们可以使用二分进行快速查找。

由于最多只会使用 2 e 5 2e5 2e5 个容器,那么我们可以建立一个数组 n u m num num 保存每个容器中数字的数量。对于当前需要插入的数字 x x x,进行二分找到第一个 n u m < c [ x ] num < c[x] num<c[x] 的位置进行插入,该容器至少还剩下 1 1 1 个位置可以存放 ≥ x \ge x x 的数字,将 x x x 加入该容器中即可。

详见代码。

Code

#include <bits/stdc++.h>
#define int long long
#define double long double
using namespace std;

inline int read()
{
    int s = 0, w = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            w = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        s = s * 10 + ch - '0', ch = getchar();
    return s * w;
}

const int MAXN = 2e5 + 10;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const double eps = 1e-9;
const double PI = acos(-1.0);

int n, k;

int m[MAXN], cap[MAXN], num[MAXN];

vector<int> vec[MAXN];

signed main()
{
    n = read(), k = read();
    for (int i = 0; i < n; i++)
    {
        int x = read();
        m[x]++;  // m[x]表示数字x的数量
    }
    for (int i = 1; i <= k; i++)
    {
        cap[i] = read();
    }
    // maxV用来保存容器的数量
    int maxV = 0;
    // 从后往前遍历
    for (int i = k; i >= 1; i--)
    {
        // 处理m[i]个数字i
        while (m[i])
        {
            // 二分找到第一个num<c[i]的容器
            int l = 0, r = MAXN;
            int ans;
            while (l <= r)
            {
                int mid = l + r >> 1;
                if (num[mid] < cap[i])
                {
                    ans = mid;
                    r = mid - 1;
                }
                else
                {
                    l = mid + 1;
                }
            }
            // 更新容器数量
            maxV = max(maxV, ans);
            // 更新该容器大小
            num[ans]++;
            // 减少一个数字i
            m[i]--;
            // 将数字i加入容器ans中
            vec[ans].push_back(i);
        }
    }
    // 输出答案
    cout << maxV + 1 << endl;
    for (int i = 0; i <= maxV; i++)
    {
        cout << vec[i].size();
        for (auto x : vec[i])
        {
            cout << " " << x;
        }
        cout << endl;
    }
    return 0;
}

【END】感谢观看!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值