Loading [MathJax]/extensions/tex2jax.js

AtCoder Grand Contest 041 – 解题报告

A – Table Tennis Training

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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 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;
}
// 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; }
// 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\) 可以提供的票数。最后算这个票数是否能使之入围即可。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 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;
}
// 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; }
// 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\),然后拼在对角线上即可。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 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;
}
// 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; }
// 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))\) 即为答案。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 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;
}
// 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; }
// 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 *