Educational Codeforces Round 59 解题报告 (CF1107)

C – Brutality

题如其名,相当暴力。直接暴力排序每一个连续段的值,然后计数统计即可。

// C.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 2e5 + 2000;
int n, k, arr[MAX_N];
char str[MAX_N];
bool cmp(int a, int b)
{
    return a > b;
}
int main()
{
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; i++)
        scanf("%d", &arr[i]);
    scanf("%s", str + 1);
    char previousVal = str[1];
    long long previousKey = 1;
    for (int i = 1; i <= n; i++)
    {
        if (str[i] != previousVal)
            sort(arr + previousKey, arr + i, cmp), previousKey = i;
        previousVal = str[i];
    }
    sort(arr + previousKey, arr + n + 1, cmp);
    long long tim = 0, ans = 0;
    char lastkey = str[1];
    for (int i = 1; i <= n; i++)
    {
        if (str[i] != lastkey)
            tim = 0, lastkey = str[i];
        if (tim < k)
            tim++, ans += arr[i];
    }
    printf("%lld", ans);
    return 0;
}

D – Compression

暴力题的又一典范。

枚举\(n\)的因数,然后在\((\frac{n}{i})^2\)的格子里进行前缀和验证即可:每一块的和要么是块长的平方要么就是 0.

// D.cpp
// Fuck codeforces.
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 5300;
int n, mat[MAX_N][MAX_N], prefix[MAX_N][MAX_N], upb = 0x3f3f3f3f;
int getNum(char ch)
{
    if (ch <= '9' && ch >= '0')
        return ch - '0';
    return 10 + ch - 'A';
}
bool check(int blk)
{
    int tot = n / blk;
    for (int i = 1; i <= tot; i++)
        for (int j = 1; j <= tot; j++)
        {
            int x = blk * i, y = blk * j;
            int ans = prefix[x][y] - prefix[x - blk][y] - prefix[x][y - blk] + prefix[x - blk][y - blk];
            if (ans == blk * blk || ans == 0)
                continue;
            else
                return false;
        }
    return true;
}
int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n / 4; j++)
        {
            char ch = getchar();
            while (ch == '\n')
                ch = getchar();
            int num = getNum(ch);
            for (int k = 0; k < 4; k++)
                mat[i][(j - 1) * 4 + k + 1] = (num >> (3 - k)) & 1;
        }
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            prefix[i][j] = prefix[i - 1][j] + prefix[i][j - 1] - prefix[i - 1][j - 1] + mat[i][j];
    // horizontial
    for (int i = 1; i <= n; i++)
    {
        int lst = 1;
        for (int j = 1; j <= n; j++)
            if (mat[i][j] != mat[i][lst])
                upb = min(upb, j - lst + 1), lst = j;
    }
    for (int i = 1; i <= n; i++)
    {
        int lst = 1;
        for (int j = 1; j <= n; j++)
            if (mat[j][i] != mat[lst][i])
                upb = min(upb, j - lst + 1), lst = j;
    }
    int ans = 1;
    for (int i = 2; i <= n; i++)
        if (n % i == 0 && check(i))
            ans = max(ans, i);
    printf("%d", ans);
    return 0;
}

E – Vasya and Binary String

考虑进行 DP。设状态 \(dp[st][ed][prefix]\) ,意义为串在\([st, ed]\)之间其中\(st\)已经连续到了\(prefix\)个了。枚举中间位\(i\),考虑以下转移:

  • 如果开始位的字符与此时的字符一致,即\(str[st] == str[i]\),可以考虑将\(str[st+1, i-1]\)作为独立子问题进行递归,然后对于右边剩余的段\(str[i, ed]\),相当于连续个数加一,所以继续递归下去:\(dp(i, ed, prefix+1)\)。
  • 如果不一致瞎转移即可。
// E.cpp
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MAX_N = 110;
ll n, arr[MAX_N], dp[MAX_N][MAX_N][MAX_N];
char str[MAX_N];

ll dfs(int st, int ed, int pre)
{
    if (ed - st <= 0)
        return 0;
    if (ed - st == 1)
        return arr[pre];
    ll &ans = dp[st][ed][pre];
    if (ans != 0)
        return ans;
    ans = arr[pre] + dfs(st + 1, ed, 1);
    for (int i = st + 1; i < ed; i++)
        if (str[i] == str[st])
            ans = max(ans, dfs(st + 1, i, 1) + dfs(i, ed, pre + 1));
    return ans;
}

int main()
{
    scanf("%lld%s", &n, str);
    for (int i = 1; i <= n; i++)
        scanf("%lld", &arr[i]);
    printf("%lld", dfs(0, n, 1));
    return 0;
}

F – Vasya and Endless Credits

神仙题之一。注意到贷款顺序肯定优先贷\(b\)小的贷款项目,考虑进行 DP,设计状态\(dp[i][j]\),意义为「考虑了前\(i\)个元素且打算还完\(j\)个贷款的最大获利」。所以可以考虑几种转移:

  • 考虑换光或者不借,\(dp[i][0] = dp[i-1][0] + curt, curt = max(0, a_i – b_i*k_i)\),取最大值可以直接决策。
  • 考虑不换一部分,枚举\(j\)为打算还的项目。\(dp[i][j] = max(dp[i-1][j]+curt, dp[i-1][j-1]+a_i-b_i*(j_i-1))\)。

写代码吧。

// F.cpp
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;

const int MAX_N = 550;
struct credit
{
    ll a, b, k;
    bool operator<(const credit &crd) const { return b > crd.b; }
} credits[MAX_N];
int n;
ll dp[MAX_N][MAX_N];

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%lld%lld%lld", &credits[i].a, &credits[i].b, &credits[i].k);
    sort(credits + 1, credits + 1 + n);
    for (int i = 1; i <= n; i++)
        dp[0][i] = -1e9;
    for (int i = 1; i <= n; i++)
    {
        ll curt = max(0LL, credits[i].a - credits[i].b * credits[i].k);
        dp[i][0] = dp[i - 1][0] + curt;
        for (int j = 1; j <= i; j++)
            dp[i][j] = max(dp[i - 1][j] + curt, dp[i - 1][j - 1] + credits[i].a - credits[i].b * (j - 1));
    }
    ll ans = 0;
    for (int i = 0; i <= n; i++)
        ans = max(ans, dp[n][i]);
    printf("%lld", ans);
    return 0;
}

Leave a Reply

Your email address will not be published. Required fields are marked *