Loading [MathJax]/extensions/tex2jax.js

Codeforces Round #551 (Div. 2) 解题报告 (CF1153)

C – Serval and Parenthesis Sequence

这道题主要是运用贪心。首先我们可以确定以下几种情况是肯定无解的:

  • 第一个字符是 ‘)’,最后一个字符是 ‘(‘。
  • 字符串长度为奇数。

我们发现整个字符串\(s[1 \dots n]\)中,第一个和最后一个字符一定要是 ‘(‘ 和 ‘)’。所以我们只用关心\(s[2 \dots n-1]\)就好。统计需要补充的括号个数:也就是\((n-2)\)减去现已确定的括号个数,分\(remL\)为左括号需要补充的个数、\(remR\)为右括号需要补充的个数。

之后可以考虑贪心替换,优先更换左括号,再更换右括号。最后验一遍即可。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// C.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 3e5 + 2000;
int len, n;
char str[MAX_N];
void goFuck()
{
printf(":("), exit(0);
}
int main()
{
scanf("%d", &len), scanf("%s", str), n = len;
int remL = 0, remR = 0;
if ((len & 1) || ((len - 2) & 1) || str[0] == ')' || str[len - 1] == '(')
goFuck();
str[0] = '(', str[len - 1] = ')';
len -= 2;
for (int i = 1; i < n - 1; i++)
if (str[i] == '(')
remL++;
else if (str[i] == ')')
remR++;
remL = len / 2 - remL, remR = len / 2 - remR;
for (int i = 1; i < n - 1; i++)
if (str[i] == '?' && remL)
str[i] = '(', remL--;
else if (str[i] == '?' && remR)
str[i] = ')', remR--;
if (remL || remR)
goFuck();
int tot = 0;
for (int i = 0; i < n; i++)
{
if (str[i] == '(')
tot++;
else
tot--;
if (tot == 0 && i != n - 1)
goFuck();
}
if (tot != 0)
goFuck();
cout << str;
return 0;
}
// C.cpp #include <bits/stdc++.h> using namespace std; const int MAX_N = 3e5 + 2000; int len, n; char str[MAX_N]; void goFuck() { printf(":("), exit(0); } int main() { scanf("%d", &len), scanf("%s", str), n = len; int remL = 0, remR = 0; if ((len & 1) || ((len - 2) & 1) || str[0] == ')' || str[len - 1] == '(') goFuck(); str[0] = '(', str[len - 1] = ')'; len -= 2; for (int i = 1; i < n - 1; i++) if (str[i] == '(') remL++; else if (str[i] == ')') remR++; remL = len / 2 - remL, remR = len / 2 - remR; for (int i = 1; i < n - 1; i++) if (str[i] == '?' && remL) str[i] = '(', remL--; else if (str[i] == '?' && remR) str[i] = ')', remR--; if (remL || remR) goFuck(); int tot = 0; for (int i = 0; i < n; i++) { if (str[i] == '(') tot++; else tot--; if (tot == 0 && i != n - 1) goFuck(); } if (tot != 0) goFuck(); cout << str; return 0; }
// C.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 3e5 + 2000;
int len, n;
char str[MAX_N];
void goFuck()
{
    printf(":("), exit(0);
}
int main()
{
    scanf("%d", &len), scanf("%s", str), n = len;
    int remL = 0, remR = 0;
    if ((len & 1) || ((len - 2) & 1) || str[0] == ')' || str[len - 1] == '(')
        goFuck();
    str[0] = '(', str[len - 1] = ')';
    len -= 2;
    for (int i = 1; i < n - 1; i++)
        if (str[i] == '(')
            remL++;
        else if (str[i] == ')')
            remR++;
    remL = len / 2 - remL, remR = len / 2 - remR;
    for (int i = 1; i < n - 1; i++)
        if (str[i] == '?' && remL)
            str[i] = '(', remL--;
        else if (str[i] == '?' && remR)
            str[i] = ')', remR--;
    if (remL || remR)
        goFuck();
    int tot = 0;
    for (int i = 0; i < n; i++)
    {
        if (str[i] == '(')
            tot++;
        else
            tot--;
        if (tot == 0 && i != n - 1)
            goFuck();
    }
    if (tot != 0)
        goFuck();
    cout << str;
    return 0;
}

D – Serval and Rooted Tree

一道比较有思维难度的好题。

这其实就是一颗决策树,让你填数字进去实现决策最优。我们可以考虑贪心地进行树形 DP,设\(dp[i]\)为答案的贡献,意义为「比答案大的个数」。这样我们可以进行 DP:

\[ dp[u] = \begin{cases} \sum_{i \in son} dp[i], u \in min \\ min_{i \in son} dp[i] \end{cases}, u \in max \]

当然,额外的,叶子节点的值统统为一。最后的答案是 \(k – dp[1] + 1\)。

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 = 3e5 + 2000;
int head[MAX_N], current, n, k, fa[MAX_N], deg[MAX_N], dp[MAX_N];
bool mark[MAX_N];
struct edge
{
int to, nxt;
} edges[MAX_N << 1];
void addpath(int src, int dst)
{
edges[current].to = dst, edges[current].nxt = head[src];
head[src] = current++;
}
void dfs(int u)
{
if (u != 1 && deg[u] == 1)
{
k++, dp[u] = 1;
return;
}
if (mark[u])
dp[u] = 0x3f3f3f3f;
else
dp[u] = 0;
for (int i = head[u]; i != -1; i = edges[i].nxt)
{
dfs(edges[i].to);
if (mark[u])
dp[u] = min(dp[u], dp[edges[i].to]);
else
dp[u] += dp[edges[i].to];
}
}
int main()
{
memset(head, -1, sizeof(head));
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &mark[i]);
for (int i = 2; i <= n; i++)
scanf("%d", &fa[i]), addpath(fa[i], i), deg[fa[i]]++, deg[i]++;
dfs(1);
printf("%d", k - dp[1] + 1);
return 0;
}
// D.cpp #include <bits/stdc++.h> using namespace std; const int MAX_N = 3e5 + 2000; int head[MAX_N], current, n, k, fa[MAX_N], deg[MAX_N], dp[MAX_N]; bool mark[MAX_N]; struct edge { int to, nxt; } edges[MAX_N << 1]; void addpath(int src, int dst) { edges[current].to = dst, edges[current].nxt = head[src]; head[src] = current++; } void dfs(int u) { if (u != 1 && deg[u] == 1) { k++, dp[u] = 1; return; } if (mark[u]) dp[u] = 0x3f3f3f3f; else dp[u] = 0; for (int i = head[u]; i != -1; i = edges[i].nxt) { dfs(edges[i].to); if (mark[u]) dp[u] = min(dp[u], dp[edges[i].to]); else dp[u] += dp[edges[i].to]; } } int main() { memset(head, -1, sizeof(head)); scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &mark[i]); for (int i = 2; i <= n; i++) scanf("%d", &fa[i]), addpath(fa[i], i), deg[fa[i]]++, deg[i]++; dfs(1); printf("%d", k - dp[1] + 1); return 0; }
// D.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 3e5 + 2000;
int head[MAX_N], current, n, k, fa[MAX_N], deg[MAX_N], dp[MAX_N];
bool mark[MAX_N];
struct edge
{
    int to, nxt;
} edges[MAX_N << 1];
void addpath(int src, int dst)
{
    edges[current].to = dst, edges[current].nxt = head[src];
    head[src] = current++;
}
void dfs(int u)
{
    if (u != 1 && deg[u] == 1)
    {
        k++, dp[u] = 1;
        return;
    }
    if (mark[u])
        dp[u] = 0x3f3f3f3f;
    else
        dp[u] = 0;
    for (int i = head[u]; i != -1; i = edges[i].nxt)
    {
        dfs(edges[i].to);
        if (mark[u])
            dp[u] = min(dp[u], dp[edges[i].to]);
        else
            dp[u] += dp[edges[i].to];
    }
}
int main()
{
    memset(head, -1, sizeof(head));
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &mark[i]);
    for (int i = 2; i <= n; i++)
        scanf("%d", &fa[i]), addpath(fa[i], i), deg[fa[i]]++, deg[i]++;
    dfs(1);
    printf("%d", k - dp[1] + 1);
    return 0;
}

F – Serval and Bonus Problem

这道题好神仙啊。

我们枚举\(2n-1\)个区间,每一段区间的期望长度是\(\frac{l}{2n+1}\)。我们可以在期望意义下认为这些区间长度相等。枚举时,我们可以记 A 为左边点的个数,B 为右边点的个数:

我们考虑这一段区间是没有点的。我们现在要算这种情况的概率。这种情况的条件:

  1. 左边有 A 个点,右边有 B 个点。
  2. A 个点中选 k 或更多的点与 B 个点中选 k 或更多的点的配对。
  3. A 中剩下的点自行配对,B 也是如此。

我们可以开始推式子了。注意到段内配对出现较多,我们定义\(f(x)\)为「\(x\)个点中自行配对的方案数」:

\[ f(x) = \begin{cases} \prod_{i = 1}^\frac{x}{2} (x – (2i-1)), \text{x为偶数} \\ 0, \text{x为奇数} \end{cases} \]

现在推概率,概率就是上面描述的三种情况,除掉\(f(2n)\)即可:

\[ P(A, B, k) = \frac{ {A \choose k } {B \choose k} k! f(A) f(B) }{f(2n)} \]

其中\(k!\)为这些选中的\(2k\)个点的配对个数。现在推期望:

\[ E(A, B, k) = len * \sum_{i = k}^{min(A,B)} P(A,B,i) \\ E(A, B, k) = \frac{l}{2n+1} * \sum_{i = k}^{min(A,B)} \frac{ {A \choose i } {B \choose i} i! f(A) f(B) }{f(2n)} \]

推完了,求和即可。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// F.cpp
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MAX_N = 5100, mod = 998244353;
ll n, k, l, f[MAX_N << 1], level[MAX_N], level_rev[MAX_N];
ll quick_pow(ll bas, ll tim)
{
ll ans = 1;
while (tim)
{
if (tim & 1)
ans = ans * bas % mod;
bas = bas * bas % mod;
tim >>= 1;
}
return ans;
}
ll comb(ll x, ll y)
{
return level[x] * level_rev[x - y] % mod * level_rev[y] % mod;
}
int main()
{
scanf("%d%d%d", &n, &k, &l);
// Get Ready for the f;
f[0] = 1;
for (int i = 1; i <= n; i++)
f[2 * i] = f[2 * (i - 1)] * (2 * i - 1) % mod;
// Get ready for the levels;
level[1] = level[0] = level_rev[1] = level_rev[0] = 1;
for (int i = 2; i < MAX_N; i++)
level[i] = level[i - 1] * i % mod;
level_rev[MAX_N - 1] = quick_pow(level[MAX_N - 1], mod - 2);
for (int i = MAX_N - 1; i >= 3; i--)
level_rev[i - 1] = level_rev[i] * i % mod;
ll answer = 0;
for (int h = 2; h <= 2 * n; h++)
{
int A = h - 1, B = 2 * n - h + 1;
for (int i = k; i <= min(A, B); i++)
{
ll all = comb(A, i) * comb(B, i) % mod * level[i] % mod * f[A - i] % mod * f[B - i] % mod;
answer = (answer + all) % mod;
}
}
answer = answer * quick_pow(2 * n + 1, mod - 2) % mod * quick_pow(f[2 * n], mod - 2) % mod * l % mod;
printf("%lld", answer);
return 0;
}
// F.cpp #include <bits/stdc++.h> #define ll long long using namespace std; const int MAX_N = 5100, mod = 998244353; ll n, k, l, f[MAX_N << 1], level[MAX_N], level_rev[MAX_N]; ll quick_pow(ll bas, ll tim) { ll ans = 1; while (tim) { if (tim & 1) ans = ans * bas % mod; bas = bas * bas % mod; tim >>= 1; } return ans; } ll comb(ll x, ll y) { return level[x] * level_rev[x - y] % mod * level_rev[y] % mod; } int main() { scanf("%d%d%d", &n, &k, &l); // Get Ready for the f; f[0] = 1; for (int i = 1; i <= n; i++) f[2 * i] = f[2 * (i - 1)] * (2 * i - 1) % mod; // Get ready for the levels; level[1] = level[0] = level_rev[1] = level_rev[0] = 1; for (int i = 2; i < MAX_N; i++) level[i] = level[i - 1] * i % mod; level_rev[MAX_N - 1] = quick_pow(level[MAX_N - 1], mod - 2); for (int i = MAX_N - 1; i >= 3; i--) level_rev[i - 1] = level_rev[i] * i % mod; ll answer = 0; for (int h = 2; h <= 2 * n; h++) { int A = h - 1, B = 2 * n - h + 1; for (int i = k; i <= min(A, B); i++) { ll all = comb(A, i) * comb(B, i) % mod * level[i] % mod * f[A - i] % mod * f[B - i] % mod; answer = (answer + all) % mod; } } answer = answer * quick_pow(2 * n + 1, mod - 2) % mod * quick_pow(f[2 * n], mod - 2) % mod * l % mod; printf("%lld", answer); return 0; }
// F.cpp
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MAX_N = 5100, mod = 998244353;

ll n, k, l, f[MAX_N << 1], level[MAX_N], level_rev[MAX_N];

ll quick_pow(ll bas, ll tim)
{
    ll ans = 1;
    while (tim)
    {
        if (tim & 1)
            ans = ans * bas % mod;
        bas = bas * bas % mod;
        tim >>= 1;
    }
    return ans;
}

ll comb(ll x, ll y)
{
    return level[x] * level_rev[x - y] % mod * level_rev[y] % mod;
}

int main()
{
    scanf("%d%d%d", &n, &k, &l);
    // Get Ready for the f;
    f[0] = 1;
    for (int i = 1; i <= n; i++)
        f[2 * i] = f[2 * (i - 1)] * (2 * i - 1) % mod;

    // Get ready for the levels;
    level[1] = level[0] = level_rev[1] = level_rev[0] = 1;
    for (int i = 2; i < MAX_N; i++)
        level[i] = level[i - 1] * i % mod;
    level_rev[MAX_N - 1] = quick_pow(level[MAX_N - 1], mod - 2);
    for (int i = MAX_N - 1; i >= 3; i--)
        level_rev[i - 1] = level_rev[i] * i % mod;

    ll answer = 0;

    for (int h = 2; h <= 2 * n; h++)
    {
        int A = h - 1, B = 2 * n - h + 1;
        for (int i = k; i <= min(A, B); i++)
        {
            ll all = comb(A, i) * comb(B, i) % mod * level[i] % mod * f[A - i] % mod * f[B - i] % mod;
            answer = (answer + all) % mod;
        }
    }
    answer = answer * quick_pow(2 * n + 1, mod - 2) % mod * quick_pow(f[2 * n], mod - 2) % mod * l % mod;
    printf("%lld", answer);
    return 0;
}

Leave a Reply

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