Educational Codeforces Round 86 個人題解

Educational Codeforces Round 86 個人題解

1342A - Road To Zero(簡單分類討論)

題意:給兩個整數 xx​yy​,需要把這兩個數都變成 00​,現在有 aa​bb​ 兩個操作,其中 aa​ 操作花費 aa​ 的代價將其中一個數字 +1+1​ 或者 1-1​bb​ 操作花費 bb​ 的代價將兩個數字同時 +1+1​ 或者 1-1​,問最小的代價是多少,需要回答 tt​ 個案例。

範圍1t100,0x,y1e9,1a,b1e91 \le t \le 100, 0 \le x, y \le 1e9, 1 \le a, b \le 1e9

題解

顯然對 xxyy 兩個數字都進行一次 aa 操作,其效果等價於進行了一次 bb 操作,如果 2a<b2*a < bbb 操作完全可以被 aa 操作取代,否則就需要進行 xy|x-y|aa 操作消除兩數的差異再進行 min(x,y)min(x, y)bb 操作。

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(構造)

題意:給一個 0101​tt​,需要找到一個長度不超過 2t2*|t|​0101​ss​ 滿足:tt​ss​ 的子序列且 ss​ 擁有最小的循環長度。只需要給出一個滿足條件的 ss​ ,需要回答 tt​ 個案例。

範圍1T100,1t1001 \le T \le 100, 1 \le |t| \le 100

題解

不要求最優解,考慮找一下規律。可以發現如果 tt 中只包含 11 或者只包含 00,那麼 ss 也只需要包含 11 或者只包含 00,循環長度爲 11;如果 tt 中既有 00 也有 11,那麼 ss 可以構造成以 "01""01" 爲最小循環子串的長度爲 2t2*|t|0101 串,循環長度爲 22,這樣對於 tt 中的每一位都可以在一個 "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(數學+前綴和)

題意:有 tt​ 個案例,每個案例給兩個整數 aa​bb​,以及 qq​ 個詢問,每個詢問給出 ll​rr​,需要計算在區間 [l,r][l, r]​ 中有多少個數字 xx​ 滿足 x%a%b=x%b%ax\%a\%b = x\%b\%a​

範圍1t100,1a,b200,1q500,1lr1e181 \le t \le 100, 1 \le a, b \le 200, 1 \le q \le 500, 1 \le l \le r \le 1e18

題解

可以發現對於給定的 aabb,從小到大滿足條件的 xx 以一個週期進行循環,而這個週期就是 aabb 的公倍數,每個週期內滿足條件的 xx 數量相同。顯然 aba*baabb 的公倍數,且 ab4e4a*b \le 4e4,那麼我們就可以預處理出一個週期內滿足條件 xx 的前綴和,因此就可以對所有的查詢區間 [l,r][l, r] 進行前綴和差分求解,ans=pre[y]pre[x1]ans = pre[y]-pre[x-1]​

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(二分)

題意:有 nn 個數字 m1mnm_1\sim m_n,並且有一種容器,一個容器只能存放 c1c_11\ge1 的數字,c2c_22\ge2 的數字,…,ckc_kk\ge k 的數字,問至少需要多少個容器才能裝下所有數字。

範圍1n,k2e5,1mik,nc1c2...ck11\le n, k \le 2e5, 1 \le m_i \le k, n \ge c_1 \ge c_2 \ge ... \ge c_k \ge 1

題解

考慮到數據範圍,我們不能夠對每個容器的各個檔位去找數字存放,也不能對於每個數字直接遍歷去找哪個容器還可以存放,不過可以利用二分進行優化遍歷的過程。

首先我們對數字進行排序,從大到小的進行處理,對於每個數字需要找到哪個容器可以容納下該數字,而進行遍歷的效率太低。

基於貪心的想法,一個容器需要儘可能多的存放數字,實在沒辦法了才使用新的容器,那麼最後每個容器中的數字數量會呈現遞減的形式,對於這樣的一個序列我們可以使用二分進行快速查找。

由於最多隻會使用 2e52e5 個容器,那麼我們可以建立一個數組 numnum 保存每個容器中數字的數量。對於當前需要插入的數字 xx,進行二分找到第一個 num<c[x]num < c[x] 的位置進行插入,該容器至少還剩下 11 個位置可以存放 x\ge x 的數字,將 xx 加入該容器中即可。

詳見代碼。

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】感謝觀看!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章