Codeforces Round #500 (Div. 1) 「based on EJOI」 – 解题报告

A – Photo of The Sky

比较显然,我们需要选两个点集,最后贡献就是两个点集的极差积。排个序,并且把每个长度为 \(n\) 的区间都给做一遍即可。

// A.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 1e5 + 200;

int n, ai[MAX_N << 1];

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= (n << 1); i++)
        scanf("%d", &ai[i]);
    sort(ai + 1, ai + 1 + (n << 1));
    long long ans = 1e18;
    for (int i = 1; i <= n; i++)
        ans = min(ans, 1LL * (ai[i + n - 1] - ai[i]) * (ai[n << 1] - (i == 1 ? ai[n + 1] : ai[1])));
    printf("%lld\n", ans);
    return 0;
}

B – Chemical table

妈的想了半天最后发现用二分图来做,气死我了。

考虑行号连至列号,画出一个二分图。思考两条边组成的三元组的意义,比如说两个左部点 \(u, v\)、一个右部点 \(w\),发现右部点可以将 \(u, v\) 所连接的右部点做到一个「共享」的功能,因为对于一行的点 \(u, v\) 来说,\(u, v\) 的列信息(相当于一个 bitset)是可以并起来的,所以一个二分图连通块可以使得整个连通块 work。

答案就是连通块个数减去一个 \(1\)。

// B.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 4e5 + 200;

typedef pair<int, int> pii;

int n, m, q, sumx[MAX_N], sumy[MAX_N], color[MAX_N];
pii pts[MAX_N];
vector<int> G[MAX_N];

void dfs(int u, int org)
{
    color[u] = org;
    for (auto v : G[u])
        if (color[v] == 0)
            dfs(v, org);
}

int main()
{
    scanf("%d%d%d", &n, &m, &q);
    for (int i = 1, u, v; i <= q; i++)
        scanf("%d%d", &u, &v), G[u].push_back(v + n), G[v + n].push_back(u);
    int ans = 0;
    for (int i = 1; i <= n + m; i++)
        if (color[i] == 0)
            dfs(i, ++ans);
    printf("%d\n", ans - 1);
    return 0;
}

C – Hills

一个 SB DP,比上一题难(汗)。

考虑设计状态,发现可以直接 \(\Theta(n^2)\) 艹过去。发现三个状态就够了:放置、未放置、已下降。随便转移即可。

// C.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 5050;

int n, ai[MAX_N], dp[MAX_N][MAX_N][3];

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &ai[i]);
    memset(dp, 0x3f, sizeof(dp));
    dp[0][0][0] = 0;
    for (int i = 1; i <= n; i++)
    {
        dp[i][0][0] = 0;
        for (int j = 1; j <= (i + 1) >> 1; j++)
        {
            if (ai[i] > ai[i - 1])
                dp[i][j][1] = min(dp[i][j][1], dp[i - 1][j - 1][0]);
            if (i != 1)
            {
                if (ai[i] > ai[i - 2] - 1)
                    dp[i][j][1] = min(dp[i][j][1], dp[i - 1][j - 1][2]);
                else
                    dp[i][j][1] = min(dp[i][j][1], dp[i - 1][j - 1][2] + ai[i - 2] - 1 - ai[i] + 1);
            }
            if (ai[i] <= ai[i - 1])
                dp[i][j][1] = min(dp[i][j][1], dp[i - 1][j - 1][0] + ai[i - 1] - ai[i] + 1);
            // choose;
            dp[i][j][0] = min(dp[i][j][0], min(dp[i - 1][j][2], dp[i - 1][j][0]));
            if (ai[i] < ai[i - 1])
                dp[i][j][0] = min(dp[i][j][0], dp[i - 1][j][1]);
            if (ai[i] >= ai[i - 1])
                dp[i][j][2] = min(dp[i][j][2], min(dp[i - 1][j][1], dp[i - 1][j][0]) + ai[i] - ai[i - 1] + 1);
        }
    }
    for (int i = 1; i <= (n + 1) >> 1; i++)
        printf("%d ", min(dp[n][i][0], min(dp[n][i][1], dp[n][i][2])));
    puts("");
    return 0;
}

D – AB-Strings

首先,连续的字符是可以压缩的,可以用链表。用完链表之后,发现如果头部不同,那么就换奇数个单元;如果相同的话,选奇数、偶数个即可。

但是,这样不一定是全局最优的。如果想要当前进行减少的话,这样显然是最优的;但是,全局最优要求我们每一次操作能尽量使这两个串大小相等,这样下一步才会较优。

代码没过,也很难写,就不挂了。

Leave a Reply

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