zoukankan      html  css  js  c++  java
  • Codeforces Round #604 (Div. 1)

    Codeforces Round #604 (Div. 1)

    A

    先把原序列去重弄成二元组 ((x,c_x)),即解了 (x) 题的有 (c_x) 人。这样的处理方便解决“严格大于”的限制,只要每种奖牌取一些二元组即可。

    然后找到人数一半的位置把后面的仍掉。接下来枚举金牌的人数,然后从后往前截取尽可能短的一段当作铜牌(需满足人数多于金牌),看看中间的一段人数是否多于金牌人数。如果多了就说明满足条件,更新一下答案

    #include <bits/stdc++.h>
    using namespace std;
    void read (int &x) {
        char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
        while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
    } const int N = 4e5 + 5;
    int n, a[N], c[N];
    struct qwq { int x, c; } b[N];
    signed main() {
        int T; read (T);
        while (T--) {
            read (n);
            for (int i = 1; i <= n; ++i) read (a[i]);
            int m = 0;
            for (int i = 1; i <= n; ++i)
                if (a[i] == a[i - 1]) ++b[m].c;
                else b[++m].x = a[i], b[m].c = 1;
            int p, x = 0, y = 0, z = 0, s = 0, o = 0;
            for (p = 1; p <= m; ++p) {
                s += b[p].c; if (s * 2 > n) break;
            } --p;
            for (int i = 1; i <= p; ++i) c[i] = c[i - 1] + b[i].c;
            for (int i = 1, j = p; i < j; ++i) {
                int tx = c[i];
                while (c[p] - c[j - 1] <= tx && j > i) --j;
                if (i == j) break;
                int ty = c[j - 1] - c[i], tz = c[p] - c[j - 1];
                if (ty > tx && tx + ty + tz > o)
                    o = tx + ty + tz, x = tx, y = ty, z = tz;
            }
            printf ("%d %d %d
    ", x, y, z);
        }
    }
    
    

    B

    抓住 (0)(3) 只能分别和一种数相邻的性质。“(01)” 可以连续相间分布,中间夹有的其他数字移到两边不影响结果。于是方案可以描述为 ((1),0,1,0,1,...0,1|2,1,2,1,...2,1,|2,3,2,3,...2,3,(2))

    (one=b-a)(two=c-d),由于两头可以放或不放 (1,2),所以优解的条件是 (|one-two|leq 1 &one,twoge0)。但还有一个特殊情况,只有 (0,1) 或只有 (2,3),且 (one=-1)(two=-1),这时在两头都放 (0)(3) 也合法。然后按上述序列构造即可

    #include <bits/stdc++.h>
    using namespace std;
    void read (int &x) {
        char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
        while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
    } const int N = 1e5 + 5;
    #define pc(x) putchar (x), putchar (' ')
    signed main() {
        int a, b, c, d;
        read (a), read (b), read (c), read (d);
        b -= a, c -= d;
        if (b >= 0 && c >= 0 && abs (b - c) <= 1) {
            puts ("YES");
            if (b > c) pc ('1');
            for (int i = 1; i <= a; ++i) pc ('0'), pc ('1');
            for (int i = 1; i <= min (b, c); ++i) pc ('2'), pc ('1');
            for (int i = 1; i <= d; ++i) pc ('2'), pc ('3');
            if (b < c) pc ('2');
        } else if (a == 0 && b == 0 && c == -1) {
            puts ("YES");
            pc ('3'); for (int i = 1; i < d; ++i) pc ('2'), pc ('3');
         } else if (c == 0 && d == 0 && b == -1) {
            puts ("YES");
            for (int i = 1; i < a; ++i) pc ('0'), pc ('1'); pc ('0');
        }
        else puts ("NO");
    }
    
    

    C

    按照检查点分成几段,期望天数显然为每段之和。那么每一段的答案怎么算呢?

    (E(x)) 为从 (x) 开始到结束的期望步数,有 (E(x)=P_xE(x+1)+(1-P_x)E(c)+1)(c)(x) 之前的最接近的检查点,(c') 为后面的最近的检查点。一个长度为 (k) 的段可以写出 (k) 个式子(方程),对这个方程组进行消元,得到 (E(c')-E(c)=frac{1+sum_{i=1}^{k}prod_{j=1}^{i}P_j}{prod_{i=1}^{k} P_i}),即这一段的期望值。维护一些前缀和即可 (O(1)) 计算。段与段之间会多算一次,还要减去段的数量

    而每次修改只会影响 (O(1)) 的区间,(set) 维护一下就好了。

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long
    void read (int &x) {
        char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
        while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
    } const int N = 2e5 + 5, mod = 998244353;
    int qpow (int x, int y) {
        int t = 1; for (; y; y >>= 1, x = x * x % mod) if (y & 1) t = t * x % mod; return t;
    }
    int n, q, res, a[N], s[N]; set<int> t; set<int>::iterator it;
    #define inv(x) qpow (x, mod - 2)
    int f (int l, int r) {
        return ((s[r] - s[l - 1] + mod) * inv (a[r]) + a[l - 1] * inv (a[r])) % mod;
    }
    #define up(x, y) ((x += y) >= mod && (x -= mod))
    signed main() {
        read (n), read (q); a[0] = 1;
        for (int i = 1; i <= n; ++i)
            read (a[i]), a[i] = a[i] * qpow (100    , mod - 2) % mod;
        for (int i = 2; i <= n; ++i) a[i] = a[i] * a[i - 1] % mod;
        for (int i = 1; i <= n; ++i) s[i] = (s[i - 1] + a[i]) % mod;
        res = f (1, n), t.insert (1), t.insert (n + 1); int num = 1;
        for (int i = 1, x, l, r; i <= q; ++i) {
            read (x); it = t.find (x);
            if (it == t.end()) {
                t.insert (x); it = t.find (x); ++num;
                --it; l = *it; ++it, ++it, r = *it;
                up (res, mod - f (l, r - 1));
                up (res, f (l, x - 1)), up (res, f (x, r - 1));
            } else {
                --it, l = *it; ++it, ++it, r = *it;
                up (res, mod - f (l, x - 1));
                up (res, mod - f (x, r - 1));
                up (res, f (l, r - 1));
                --it, t.erase (it); --num;
            }
            printf ("%lld
    ", (res + mod - num) % mod);
        }
    }
    
    

    D

    考虑每个 ‘(’ 有多少次被算入答案中。如果 (x) 被算入,需满足 (x) 左边的 ‘(' 的数量小于右边 ')' 的数量,由此列出算式:(a) 为左侧 ‘(' 的数量,(b) 为左侧 ‘?' 的数量;(c) 为右侧 ‘)' 的数量,(d) 为右侧 ‘?' 的数量,可以枚举左侧 ‘?’ 变为 ‘(‘ 的数量和右侧 '?' 变成 ')' 的数量,有 (number=sum_{a+i<c+j}C(b,i)·C(d,j))。尽量把 (i,j) 在限制不等式中弄成同号方便合并,于是用 (d-j) 换掉 (j)。变成了这样 (number=sum_{i+j<c+d-a}C(b,i)·C(d,j))。在 (b) 中选 (i) 个,(d) 中选 (j) 个,相当于在 (b+d) 中选 (i+j) 个。这样就可以合并了,用 (k) 换掉 (i+j),得(res=sum_{k<c+d-a}C(b+d,k))

    (b+d) 为左右 '?' 的总数,最多只有两种取值,因此可以预处理这两种组合数的前缀和,然后加一加

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long
    const int N = 1e6 + 5, mod = 998244353;
    int n, in[N], p[N], s[2][N]; char a[N];
    int qpow (int x, int y) {
        int t = 1; for (; y; y >>= 1, x = x * x % mod) if (y & 1) t = t * x % mod; return t;
    }
    int C (int x, int y) {
        return x < y ? 0 : p[x] * in[y] % mod * in[x - y] % mod;
    }
    void prework () {
        p[0] = 1; for (int i = 1; i <= n; ++i) p[i] = p[i - 1] * i % mod;
        in[n] = qpow (p[n], mod - 2);
        for (int i = n; i >= 1; --i) in[i - 1] = in[i] * i % mod;
    }
    int cl[N], cr[N], sl[N], sr[N];
    signed main() {
        scanf ("%s", a + 1); n = strlen (a + 1); prework ();
        for (int i = 1; i <= n; ++i)
            cl[i] = cl[i - 1] + (a[i] == '('), sl[i] = sl[i - 1] + (a[i] == '?');
        for (int i = n; i >= 1; --i)
            cr[i] = cr[i + 1] + (a[i] == ')'), sr[i] = sr[i + 1] + (a[i] == '?');
        s[0][0] = s[1][0] = 1; int res = 0;
        for (int i = 1; i <= n; ++i) {
            s[1][i] = (s[1][i - 1] + C (sl[n] - 1, i)) % mod;
            s[0][i] = (s[0][i - 1] + C (sl[n], i)) % mod;
        }
        for (int i = 1; i <= n; ++i) if (cl[i - 1] < cr[i + 1] + sr[i + 1] && a[i] != ')')
            (res += s[a[i] == '?'][cr[i + 1] + sr[i + 1] - cl[i - 1] - 1]) %= mod;
        printf ("%lld
    ", res);
    }
    
    

    E

    先反其道而行之,如果 ((A,B,C)) 不符合条件,一定有某一个队伍赢了另外两个队。进一步,如果 (x) 队赢了 (c_x) 局,不满足条件的三元组数量 (number=sumfrac{c_x(c_x-1)}{2})。目的就是让这个东西尽量小

    现在有一些的比赛结果已经固定,相当于每个 (c_x) 有一个限制:(c_xin [l_x,r_x])(sum c_x=frac{n(n-1)}{2})。如果没有这个限制当然可以根据不等式直接取值,但加上限制后似乎没法直接贪心。(不然为什么数据范围这么小呢

    改用费用流。每场比赛一个节点,每个队伍一个节点。连这几种边:

    1、(S) 到所有比赛节点连一条容量为 (1),花费为 (0) 的边,代表这场比赛赢家只有一个。

    2、每场比赛向对应的两支队伍连边,容量 (1),花费 (0)

    3、每支队伍向 (T)(n-p_x-1) 条边,(p_x) 为固定的已经赢的场数。每条边的容量为 (1),花费为每次多赢一场后 (frac{c_x(c_x-1)}{2}) 的增量

    在这张图上求最小费用最大流就是答案

    #include <bits/stdc++.h>
    using namespace std;
    void read (int &x) {
        char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
        while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
    } const int N = 52, P = N * N, M = N * 600;
    int n, m, S, T, tot, k[N][N], c[N], idm[N][N], id[N];
    int cnt = 1, h[M], nxt[M], to[M], w[M], co[M];
    void add (int u, int v, int ww, int cc) {
        to[++cnt] = v, w[cnt] = ww, co[cnt] = cc, nxt[cnt] = h[u], h[u] = cnt;
        to[++cnt] = u, w[cnt] = 0, co[cnt] = -cc, nxt[cnt] = h[v], h[v] = cnt;
    }
    queue<int> q; int d[P], f[P], in[P], pre[P];
    int spfa () {
        memset (d, 0x3f, sizeof (d));
        memset (in, 0, sizeof (in));
        q.push (S), d[S] = 0, f[S] = 2e9;
        while (!q.empty()) {
            int u = q.front (); q.pop (), in[u] = 0;
            for (int i = h[u], v; i; i = nxt[i])
                if (w[i] && d[v = to[i]] > d[u] + co[i]) {
                    d[v] = d[u] + co[i];
                    f[v] = min (f[u], w[i]); pre[v] = i;
                    if (!in[v]) in[v] = 1, q.push (v);
                }
        }
        return (d[T] != d[T + 1]);
    }
    void update () {
        for (int x = T, i; x != S; x = to[i ^ 1]) {
            i = pre[x]; w[i] -= f[T], w[i ^ 1] += f[T];
        }
    }
    signed main() {
        read (n), read (m);
        for (int i = 1, a, b; i <= m; ++i)
            read (a), read (b), k[a][b] = 1, ++c[a];
        for (int i = 1; i <= n; ++i) id[i] = ++tot;
        for (int i = 1; i <= n; ++i)
            for (int j = i + 1; j <= n; ++j)
                if (!k[i][j] && !k[j][i]) {
                    idm[i][j] = ++tot; add (0, tot, 1, 0);
                    add (tot, id[i], 1, 0); add (tot, id[j], 1, 0);
                }
        S = 0, T = ++tot;
        for (int i = 1; i <= n; ++i)
            for (int j = c[i]; j < n; ++j) add (id[i], T, 1, j);
        while (spfa ()) update ();
        for (int i = 1; i <= n; ++i)
            for (int j = i + 1; j <= n; ++j)
                if (idm[i][j]) {
                    int x = idm[i][j], tag = 0, y = 0;
                    for (int k = h[x]; k; k = nxt[k])
                        if (to[k] == 0 && w[k] == 1) tag = 1;
                    if (!tag) continue;
                    for (int k = h[x]; k; k = nxt[k])
                        if (to[k] && w[k] == 0) y = to[k];
                    y == i ? k[i][j] = 1 : k[j][i] = 1;
                }
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= n; ++j) putchar (k[i][j] + '0'); puts ("");
        }
    }
    
    

    F

    奇怪的数学题,不如看这里,中文在那里

  • 相关阅读:
    关于feign调用请求头丢失分析
    并发下编写SQL的注意事项
    Sentinel降级规则整理
    Mybatis-Plus使用整理
    Docker各种零散命令整理
    set集合怎么保证不重复的
    idea启动项目ava heap space
    网络穿透工具--钉钉HTTP穿透
    Log4j基本使用
    ide中普通java程序打包
  • 原文地址:https://www.cnblogs.com/whx666/p/604-div1.html
Copyright © 2011-2022 走看看