Codeforces Round #604

Beautiful Regional Contest

题目链接 http://codeforces.com/contest/1264/problem/A

题意

给出区域赛中 N 支队伍做出的题数,在一定规则下发奖时,金银铜牌奖牌数总和的最大值。
要求:

  1. 合法方案中每种奖牌至少一个
  2. 金牌数严格少于银牌和铜牌数,银牌和铜牌数间没有要求
  3. 金牌队伍解题数必须严格大于银牌队伍解题数,银铜、铜铁同理。
  4. 奖牌总数和不能超过队伍总数的一半(21支队伍时奖牌数至多10,26支队伍将牌至多13)。

题解

首先根据 要求4,去除一定不可能得奖的后一半人。
因为 要求2 只要求银铜的奖牌数多于金牌,所以直接使金牌数为最少(解题最多的那些队伍),然后从剩下的队伍中找到足够多的队伍得银牌(前缀和然后二分),剩下得铜牌即可。
注意中间发现无法满足要求时及时结束代码。

代码

/*
 * Codeforces Round #604
 * Beautiful Regional Contest
 */
#include <cstdio>
#include <algorithm>
using namespace std;
int N;
int ls[400010];
void work() {
    scanf("%d", &N);
    for (int i = 0; i < N; i++) {
        scanf("%d", ls+i);
    }
    int j, nu;
    nu = ls[0];
    ls[0] = 1;
    j = 0;
    for (int i = 1; i < N; i++) {
        if (ls[i] == nu) {
            ls[j]++;
        }
        else {
            nu = ls[i];
            j++;
            ls[j] = 1;
        }
    }
    int sm = 0;
    for (int i = j; i >= 0; i--) {
        sm += ls[i];
        if (sm*2 >= N) {
            j = i;
            break;
        }
    }
    if (j < 3) {
        printf("0 0 0\n");
        return;
    }
    for (int i = 1; i < j; i++) {
        ls[i] += ls[i-1];
    }
    int a, b, c;
    a = ls[0];
    int *it;
    it = lower_bound(ls, ls+j, a*2+1);
    if (it == ls+j)
        a = b = c = 0;
    else {
        b = *it - a;
        c = ls[j-1] - *it;
        if (!a || !b || !c || a>=c) {
            a = b = c = 0;
        }
    }
    printf("%d %d %d\n", a, b, c);
}
int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        work();
    }
    return 0;
}

Beautiful Sequence

题目链接 http://codeforces.com/contest/1264/problem/B

题意

给出 0, 1, 2, 3 四种数字的个数 a, b, c, d,要求构造数列,满足相邻数字相差为1。

题解

可以知道 0 只能和 1 相邻,当有多个 0 时,一定是通过 010101 这样的序列消耗掉 0 的,对于 3 同理,通过 232323 消耗。
优先考虑用 1 消耗尽可能多的 0,用 2 消耗尽可能多的 3。
剩下的 1 和 2 一定是通过 212121 的序列消耗的。
根据上述规则构成 010121212323 序列中的三部分 0101 2121 2323。
此外,恰好多出一个 1 和 2 时可以放在开头和结尾形成 1 0101 2121 2323 2。
多出 0 和 3 时,以 0 为例,构成 0101 0 时需要保证没有 2 和 3,因为 2 和 3 都不能接在 0 的后面。

按照如上分析实现代码即可。
实际做题过程中,通过理清思路可以让编码难度直线下降。
当前思路为,优先考虑 01 和 23 成对消耗,然后考虑剩余 12 成对消耗,最后处理多余数字的情况。这是因为成对消耗可以看作是化简问题的过程,处理多余数字是特判的过程。
如果混用这两部分的话,容易出错漏情况。

代码

/*
 * Codeforces Round #604
 * Beautiful Sequence
 */
#include <cstdio>
#include <algorithm>
using namespace std;
int a, b, c, d;
int main() {
    scanf("%d %d %d %d", &a, &b, &c, &d);
    int A, B, C, D;
    int u;
    A = a;
    B = b;
    C = c;
    D = d;

    u = min(A, B);
    A -= u;
    B -= u;
    u = min(C, D);
    C -= u;
    D -= u;
    u = min(B, C);
    B -= u;
    C -= u;
    if (A > 1 || B > 1 || C > 1 || D > 1)
        goto be;
    if ((A && (c||d)) || (D && (a||b)))
        goto be;
he:
    printf("YES\n");
    if (B)
        printf("1 ");
    u = min(a, b);
    for (int i = 0; i < u; i++)
        printf("0 1 ");
    if (A)
        printf("0 ");
    u = min(b - min(a, b), c - min(c, d));
    for (int i = 0; i < u; i++)
        printf("2 1 ");
    if (D)
        printf("3 ");
    u = min(c, d);
    for (int i = 0; i < u; i++)
        printf("2 3 ");
    if (C)
        printf("2 ");
    printf("\n");
    return 0;
be:
    printf("NO\n");
    return 0;
}

一个思路不够清晰的代码

/*
 * Codeforces Round #604
 * Beautiful Sequence
 */
#include <cstdio>
#include <algorithm>
using namespace std;
int a, b, c, d;
int main() {
    scanf("%d %d %d %d", &a, &b, &c, &d);
    int A, B, C, D;
    int u;
    A = a;
    B = b;
    C = c;
    D = d;
    
    // 从这里开始
    u = min(A, B);
    A -= u;
    B -= u;
    u = min(C, D);
    C -= u;
    D -= u;
    
    if (A > 1 || D > 1)
        goto be;
    if (A && (c||d))
        goto be;
    if (D && (a||b))
        goto be;
    int uBC;
    uBC = u = min(B, C);
    B -= u;
    C -= u;
    if (B > 1 || C > 1)
        goto be;
    // 到这里
he:
    printf("YES\n");
    if (B)
        printf("1 ");
    u = min(a, b);
    for (int i = 0; i < u; i++)
        printf("0 1 ");
    if (A)
        printf("0 ");
    u = uBC;
    for (int i = 0; i < u; i++)
        printf("2 1 ");
    if (D)
        printf("3 ");
    u = min(c, d);
    for (int i = 0; i < u; i++)
        printf("2 3 ");
    if (C)
        printf("2 ");
    printf("\n");
    return 0;
be:
    printf("NO\n");
    return 0;
}

Beautiful Mirrors with queries

题目链接 https://codeforces.com/contest/1264/problem/C

题意

N 个节点依次排列,每天尝试通过一个节点,每个节点有 \(P_i\) 的概率通过,下一天尝试第 i+1 个节点,如果没通过,下一天尝试通过前面最近的存档点。
求成功通过最后一个节点需要天数的期望。
Q 次询问,每次给出一个编号 u,修改 u 是否为检查点,并且输出修改后的答案。
1 号节点始终为存档点。
题中 \(P_i\) 以 \(\frac{p_i}{100}\) 的形式给出。

题解

如果没有检查点的修改,显然可以通过如下递推式计算答案,其中 \(F_i\) 表示从起点走到 i 号点需要天数的期望。
\[ F_i = F_{i-1} + P_i*1 + (1-P_i)(F_i+1) \]
在经过了前 \(i-1\) 个节点之后,\(P_i\) 的概率一次尝试并通过,\(1-P_i\)的概率退回存档点重新开始。

或者可以用一个通项公式来求,\(E\) 表示通过全部节点的期望天数。
\[ E = P_1(1+P_2(1+\dots)+(1-P_2)(1+E)) + (1-P_1)(1+E) \]
化简式子:
\[ \begin{aligned}
E =& (P_1 + P_1P_2 +\dots+ P_1\dots P_N)\\
&+[(1-P_1)(1+E) + P_1(1-P_2)(1+E) +\dots+ P_1\dots P_{N-1}(1-P_N)(1+E)]\\
=& (P_1 + P_1P_2 +\dots+ P_1\dots P_N) + (1-P_1\dots P_N)(1+E)\\
=& (1 + P_1 + P_1P_2 +\dots+ P_1\dots P_{N-1}) + (1-P_1\dots P_N)E
\end{aligned} \]

\[
E = \frac{A}{B} = \frac{1+P_1+P_1P_2+\dots+P_1\dots P_{N-1}}{P_1P_2\dots P_N}
\]

扫描二维码关注公众号,回复: 8122456 查看本文章

对于多个检查点的情况,可以发现,检查点分开的两段可以单独处理
即存在 x 号节点是检查点时,可以分别计算 \(E_{1,x-1}\) 和 \(E_{x,N}\) 然后求和就是总答案。
更一般的,有:
\[
E_{u,v} = \frac{A_{u,v}}{B_{u,v}} = \frac{1+P_u+P_uP_{u+1}+\dots+P_u\dots P_{v-1}}{P_uP_{u+1}\dots P_v}
\]
分别计算检查点隔开的每一段即可。
在 a, c 之间添加新的检查点 b 时,删除 (a, c-1),增加 (a, b-1), (b, c-1)。
删除时相反。
可以用 set 直接维护检查点。

代码

/*
 * Codeforces Round #604
 * Beautiful Mirrors with queries
 */
#include <cstdio>
#include <set>
using namespace std;
#define Nmax 200010
typedef long long LL;
LL MD = 998244353;
LL fpw(LL a, LL b) {
    LL ans = 1;
    while (b) {
        if (b & 1)
            ans = ans * a % MD;
        a = a * a % MD;
        b >>= 1;
    }
    return ans;
}
inline LL re(LL a) {
    return fpw(a, MD - 2);
}
int N, Q;
LL P[Nmax];
LL mt[Nmax];
LL sm[Nmax];
LL ans;
set<int> st;
LL getans(int a, int b) {
    LL p;
    p = (((sm[b-2]-sm[a-1])%MD+MD)%MD
        * re(mt[a-1]) %MD + 1)%MD
        * re(mt[b-1]*re(mt[a-1])%MD) %MD;
    return p;
}
void init() {
    LL hdr;
    hdr = re(100);
    mt[0] = 1;
    sm[0] = 0;
    for (int i = 1; i <= N; i++) {
        P[i] = P[i] * hdr % MD;
        mt[i] = mt[i-1] * P[i] % MD;
        sm[i] = sm[i-1] + mt[i] % MD;
    }
    st.insert(1);
    st.insert(N+1);
    ans = (sm[N-1]+1)%MD * re(mt[N]) % MD;
}
int main() {
    scanf("%d %d", &N, &Q);
    for (int i = 1; i <= N; i++) {
        scanf("%lld", P+i);
    }
    init();
    int x, y, z;
    set<int>::iterator it;
    for (int i = 0; i < Q; i++) {
        scanf("%d", &x);
        if (st.count(x)) {
            it = st.find(x);
            it--;
            y = *it;
            it++;
            it++;
            z = *it;
            st.erase(x);
            ans = ((ans-getans(y, x))%MD+MD)%MD;
            ans = ((ans-getans(x, z))%MD+MD)%MD;
            ans = (ans+getans(y, z))%MD;
        }
        else {
            st.insert(x);
            it = st.find(x);
            it--;
            y = *it;
            it++;
            it++;
            z = *it;
            ans = ((ans-getans(y, z))%MD+MD)%MD;
            ans = (ans+getans(y, x))%MD;
            ans = (ans+getans(x, z))%MD;
        }
        printf("%lld\n", ans);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/KZNS/p/codeforces-round-604.html