AtCoder Grand Contest 041 – 解题报告

A – Table Tennis Training

思博题,考虑两端和奇偶性即可。

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

using namespace std;

typedef long long ll;

int main()
{
    ll n, A, B, ans = 0x7fffffffffffffff;
    scanf("%lld%lld%lld", &n, &A, &B);
    ans = min(A - 1, n - B) + 1 + ((B - A - 1) >> 1);
    if (!((A^B) & 1))
        ans = min(ans, (B - A) >> 1);
    printf("%lld\n", ans);
    return 0;
}

B – Voting Judges

首先要意识到这个问题是可二分的,因为如果答案更大可行、那么小答案也可行。接下来考虑写一个 check。首先要判掉单独加上都无法入围的情况,然后算比 \(a_{mid}\) 小的那些 \(a_i\) 可以提供的票数。最后算这个票数是否能使之入围即可。

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

using namespace std;

const int MAX_N = 1e5 + 200;

int n, m, V, P, ai[MAX_N];

bool check(int mid)
{
    if (ai[mid] + m < ai[P])
        return false;
    long long acc = 0;
    for (int i = P; i <= n; i++)
        if (i != mid && ai[i] < ai[mid] + m)
            acc += min(m, ai[mid] + m - ai[i]);
    return acc >= 1LL * (V - 1) * m;
}

int main()
{
    scanf("%d%d%d%d", &n, &m, &V, &P);
    for (int i = 1; i <= n; i++)
        scanf("%d", &ai[i]);
    sort(ai + 1, ai + 1 + n), reverse(ai + 1, ai + 1 + n);
    V = max(1, V - P + 1);
    int l = P, r = n, res = P;
    while (l <= r)
    {
        int mid = (l + r) >> 1;
        if (check(mid))
            res = mid, l = mid + 1;
        else
            r = mid - 1;
    }
    printf("%d\n", res);
    return 0;
}

C – Domino Quality

首先可以找出 \(3\) 的、单元数为 \(2\) 的那个情况,这个情况直接输出即可;另外,我们能自己搞出来 \(4 \to 7\) 的、单元数为 \(3\) 的情况,之后如果 \(n > 7\),则表示成 \(n = 4x + y, y > 3\),然后拼在对角线上即可。

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

using namespace std;

int n;
char buffer[1010][1010];

const char m3[3][4] =
    {"aa.",
     "..a",
     "..a"};

const char m4[4][5] =
    {"aabc",
     "ddbc",
     "bcaa",
     "bcdd"};

const char m5[5][6] =
    {"aabba",
     "bcc.a",
     "b..cb",
     "a..cb",
     "abbaa"};

const char m6[6][7] =
    {"aabc..",
     "ddbc..",
     "..aabc",
     "..ddbc",
     "bc..aa",
     "bc..dd"};

const char m7[7][8] =
    {"aabbcc.",
     "dd.dd.a",
     "..d..da",
     "..d..db",
     "dd.dd.b",
     "..d..dc",
     "..d..dc"};

void writeBuffer(int id, int x, int y)
{
    for (int i = 0; i < id; i++)
    {
        const char *str;
        switch (id)
        {
        case 3:
            str = m3[i];
            break;
        case 4:
            str = m4[i];
            break;
        case 5:
            str = m5[i];
            break;
        case 6:
            str = m6[i];
            break;
        case 7:
            str = m7[i];
            break;
        }
        for (int j = 0; str[j]; j++)
            buffer[x + i][y + j] = str[j];
    }
}

int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            buffer[i][j] = '.';
    if (n <= 2)
        puts("-1"), exit(0);
    if (n <= 7)
        writeBuffer(n, 0, 0);
    else
    {
        int base = 0;
        writeBuffer(n % 4 + 4, base, base), base += n % 4 + 4;
        while (base < n)
            writeBuffer(4, base, base), base += 4;
    }
    for (int i = 0; i < n; i++)
        printf("%s\n", buffer[i]);
    return 0;
}

D – Problem Scores

这应该是本次最有意思的题目了,之前 lcx 的某个教练群里发过。

其实就是要满足其 \(k + 1\) 前缀和严格大于其 \(k\) 后缀和,写出来即:

\[ \sum_{i = 1}^{k + 1} a_i > \sum_{i = n – k + 1}^n a_i \]

一共会有 \(n\) 个这样的限制条件,我们尝试寻找性质来缩减之。首先,对于 \(k > \lfloor \frac{n}{2} \rfloor\) 的情况,存在前缀和后缀中间重叠的部分,这个可以直接移项消掉,转化成 \(k \leq \lfloor \frac{n}{2} \rfloor\) 的情况。

再考虑利用 \(a_i \leq a_{i + 1}\) 的性质,则发现当 \(k = \lfloor \frac{n}{2} \rfloor\) 时满足,\(k \leq \lfloor \frac{n}{2} \rfloor\) 的情况可以被充分满足。

那么我们现在只需要保证 \(\sum_{i = 1}^{k + 1} a_i > \sum_{i = n – k + 1}^n a_i, k = \lfloor \frac{n}{2} \rfloor\) 即可。

现在我们对这个序列进行差分,使得 \(a_k = 1 + \sum_{i = 1}^k \Delta_i\)。首先显然有 \(\Delta \geq 0\)。其次,带回式子时,发现前缀和可以被拆解为 \(\sum \Delta_i c_i\),其中这个 \(c_i\) 很好算。考虑带入:

\[ \sum_{i = 1}^{k + 1} 1 + \sum_{j = 1}^i \Delta_j > \sum_{i = n – k + 1}^n 1 + \sum_{j = 1}^i \Delta_j \\ 1 + \sum_{i = 1}^{k + 1} \sum_{j = 1}^i \Delta_j > \sum_{i = n – k + 1}^n \sum_{j = 1}^i \Delta_j \\ \sum_{i = 1}^{k + 1} \sum_{j = 1}^i \Delta_j \geq \sum_{i = n – k + 1}^n \sum_{j = 1}^i \Delta_j \\ \sum_{j = 1}^n \Delta_j \sum_{i = 1}^{k + 1} [j \leq i] \geq \sum_{j = 1}^n \Delta_j \sum_{i = n – k + 1}^n [j \leq i] \\ \sum_{j = 1}^{k + 1} \Delta_j (k + 2 – j) \geq \sum_{j = 1}^n \Delta_j (n – \max(j, n – k + 1) + 1) \]

得到:

\[ (k + 1) \Delta_1 + \sum_{j = 2}^{k + 1} \Delta_j (k + 2 – j) \geq k\Delta_1 + \sum_{j = 2}^n \Delta_j (n – \max(j, n – k + 1) + 1) \\ \Delta_1 \geq \sum_{j = 2}^n \Delta_j (n – \max(j, n – k + 1) + 1) – \sum_{j = 2}^{k + 1} \Delta_j (k + 2 – j) \\ \Delta_1 \geq \sum_{j = 2}^n \Delta_j (n – \max(j, n – k + 1) + 1) – \sum_{j = 2}^n \Delta_j \max(0, k + 2 – j) \\ \Delta_1 \geq \sum_{j = 2}^n \Delta_j (n + 1 – (\max(j, n – k + 1) + \max(0, k + 2 – j))) \\ \Delta_1 \geq \sum_{j = 2}^n \Delta_j (n + 1 – \max(j, k + 2, n – k + 1, n + 3 – j)) \\ \Delta_1 \geq \sum_{j = 2}^n \Delta_j (n + 1 + \min(-j, – k – 2, – n + k – 1, – n – 3 + j)) \\ \Delta_1 \geq \sum_{j = 2}^n \Delta_j \min(n – j + 1, n – k – 1, k, j – 2) \\ \]

提醒一下,\(k = \lfloor \frac{n}{2} \rfloor\):

区间是离散的,所以最后 \(\min(n – j + 1, n – k – 1, k, j – 2) = \min(j – 2, n – j + 1)\)。最后整个不等式形如:

\[ \Delta_1 \geq [\Delta_2, \Delta_3, \dots \Delta_n] \cdot [0, 1, 2, 3, \dots 3, 2, 1] \]

考虑设:

\[ a = \Delta_2 + \Delta_3 + \Delta_4 + \dots + \Delta_n \\ b = [\Delta_2, \Delta_3, \dots \Delta_n] \cdot [0, 1, 2, 3, \dots 3, 2, 1] \]

发现 \(\Delta_1 \leq n – 1 – a, \Delta_1 \geq b \),那么,\(\Delta_1\) 的选择方式有 \(\max(0, n – 1 – (a + b))\) 种。\(a + b = [\Delta_2, \Delta_3, \dots \Delta_n] \cdot [1, 2, 3, 4, \dots 4, 3, 2]\),所以我们用 \(DP\) 算下和为 \(j = a + b\) 的 \(\Delta_i\) 序列的方案数,然后用方案数乘上一个 \(\max(0, n – 1 – (a + b))\) 即为答案。

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

using namespace std;

const int MAX_N = 5050;

int n, mod, dp[MAX_N], coeff[MAX_N];

int main()
{
    scanf("%d%d", &n, &mod), dp[0] = 1;
    for (int i = 1; i < n; i++)
        for (int j = min(n - i + 1, i), k = j; j < n; j++)
            dp[j] = (0LL + dp[j] + dp[j - k]) % mod;
    int ans = 0;
    for (int i = 0; i < n; i++)
        ans = (0LL + ans + 1LL * dp[i] * (n - i) % mod) % mod;
    printf("%d\n", ans);
    return 0;
}


Leave a Reply

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