zoukankan      html  css  js  c++  java
  • Codeforces Global Round 16 题解

    旅行传送门

    A. Median Maximization

    题意:给你两个正整数 \(n\)\(s\) 。求由 \(n\) 个非负整数组成的总和为 \(s\) 的序列的最大中位数。

    题目分析:显然,令前 $ \frac{n+1}{2}-1$ 个数为 \(0\) 时序列有最大中位数。

    AC代码

    #include <bits/stdc++.h>
    #define rep(i, x, y) for (register int i = (x); i <= (y); i++)
    #define down(i, x, y) for (register int i = (x); i >= (y); i--)
    using ll = long long;
    
    char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
    #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
    inline ll read()
    {
        ll x = 0, f = 1;
        char ch = getchar();
        while (!isdigit(ch))
        {
            if (ch == '-')
                f = -1;
            ch = getchar();
        }
        while (isdigit(ch))
        {
            x = x * 10 + ch - '0';
            ch = getchar();
        }
        return x * f;
    }
    
    ll solve()
    {
        ll n = read(), s = read();
        return n & 1 ? s / ceil(1.0 * n / 2) : s / (ceil(1.0 * n / 2) + 1);
    }
    
    int main(int argc, char const *argv[])
    {
        int T = read();
        while (T--)
            printf("%lld\n", solve());
        return 0;
    }
    

    B. MIN-MEX Cut

    题意:设二进制字符串的 \(MEX\) 为字符串中未出现的 \(0\)\(1\)\(2\) 中最小的数字。

    现在给你一个字符串 \(s\) ,你可以将 \(s\) 切成任意几份(分成几个子串),求所有子串片段的最小 \(MEX\) 总和。

    题目分析:要使得 \(MEX\) 总和最小,显然要将连续的 \(0\) 切分出来。另外要明确的是 \(ans \leq 2\) ,因此比较连续段 \(0\) 的段数和 \(2\) 的大小即可。

    AC代码

    #include <bits/stdc++.h>
    #define rep(i, x, y) for (register int i = (x); i <= (y); i++)
    #define down(i, x, y) for (register int i = (x); i >= (y); i--)
    #define IOS                      \
        ios::sync_with_stdio(false); \
        cin.tie(nullptr);            \
        cout.tie(nullptr);
    using namespace std;
    
    int solve()
    {
        string s;
        cin >> s;
        int len = s.length();
        int i = 0, res = 0;
        while (i < len)
        {
            while (s[i] - '0' == 0)
            {
                ++i;
                if (s[i] - '0' == 1)
                {
                    ++res;
                    break;
                }
            }
            ++i;
        }
        if (s[len - 1] - '0' == 0)
            ++res;
        return std::min(res, 2);
    }
    
    int main(int argc, char const *argv[])
    {
        int T;
        cin >> T;
        while (T--)
            printf("%d\n", solve());
        return 0;
    }
    

    C. MAX-MEX Cut

    题意:给你一个 \(2 \times n\)\(01\) 矩阵, 你可以将该矩阵切成任意几份(分成几个 \(2 \times m\) 的子矩阵),求所有子矩阵的最大 \(MEX\) 总和。

    题目分析:我们不妨枚举下 \(2 \times 2\)\(01\) 矩阵的所有情况,\({\begin{bmatrix}1&0\\0&1\end{bmatrix}}\)\({\begin{bmatrix}1&0\\0&0\end{bmatrix}}\)\({\begin{bmatrix}1&1\\0&1\end{bmatrix}}\)\({\begin{bmatrix}0&0\\1&0\end{bmatrix}}\)\({\begin{bmatrix}0&1\\1&1\end{bmatrix}}\)\({\begin{bmatrix}0&1\\0&1\end{bmatrix}}\) ,不难发现,\({\begin{bmatrix}1\\1\end{bmatrix}}\) 本身对答案是没有贡献的,但它与 \({\begin{bmatrix}0\\0\end{bmatrix}}\) 组合时,却能使 \({\begin{bmatrix}0\\0\end{bmatrix}}\) 的贡献 \(+1\) ,而 \({\begin{bmatrix}1\\0\end{bmatrix}}\)\({\begin{bmatrix}0\\1\end{bmatrix}}\))的贡献本身已经是 \(2\) 了,与其它的 \(1 \times 2\) 矩阵组合时也不会产生多的贡献。那么我们的思路已经很明确了,先找出主矩阵中的所有 \({\begin{bmatrix}1\\1\end{bmatrix}}\) 子矩阵,与它旁边空闲的 \({\begin{bmatrix}0\\0\end{bmatrix}}\) 组合,最后再扫一遍将未组合的 \(1 \times 2\) 矩阵切分出来加上贡献即是最终答案。

    AC代码

    #include <bits/stdc++.h>
    #define rep(i, x, y) for (register int i = (x); i <= (y); i++)
    #define down(i, x, y) for (register int i = (x); i >= (y); i--)
    #define IOS                      \
        ios::sync_with_stdio(false); \
        cin.tie(nullptr);            \
        cout.tie(nullptr);
    using namespace std;
    
    int solve()
    {
        int n, res = 0;
        string s1, s2;
        cin >> n >> s1 >> s2;
        std::vector<int> vis(n);
        rep(i, 0, n - 1)
        {
            if (s1[i] == '1' && s2[i] == '1')
                if (i > 0 && s1[i - 1] == '0' && s2[i - 1] == '0' && !vis[i - 1] && !vis[i])
                    res += 2, vis[i - 1] = vis[i] = 1;
                else if (i < n && s1[i + 1] == '0' && s2[i + 1] == '0' && !vis[i] && !vis[i + 1])
                    res += 2, vis[i] = vis[i + 1] = 1;
        }
        rep(i, 0, n - 1)
        {
            if (s1[i] != s2[i])
                res += 2, vis[i] = 1;
            else if (s1[i] == '0' && s2[i] == '0' && !vis[i])
                ++res, vis[i] = 1;
        }
        return res;
    }
    
    int main(int argc, char const *argv[])
    {
        int T;
        cin >> T;
        while (T--)
            printf("%d\n", solve());
        return 0;
    }
    

    D1. Seating Arrangements (easy version)

    题意:有 \(n \times m\) 个人和 \(n \times m\) 个座位,座位编号如题面所示。每个人的视力对应一个值 \(a_i\)\(a_i\)越小的人座位越靠前。从第 \(1\) 个人至 第 \(n \times m\) 个人依次就座,每个人就座时的代价为该行经过的人数,求如何安排座位可使代价最小化。

    题目分析: 显然,对某一行来说,若同一个数值连续出现,那么他们之间不会产生任何代价(把编号较小的放进靠后的位置)。此外,数值大的在数值小的之前出现,也不会产生任何代价(数值大的先坐进靠后的位置去了)。那么会对答案产生贡献的不就一种情况了吗?数值小的在数值大的之前!这是什么?正序对?树状数组走起。

    需要注意: \(a_i\)很大,离散一下。

    AC代码

    #include <bits/stdc++.h>
    #define rep(i, x, y) for (register int i = (x); i <= (y); i++)
    #define down(i, x, y) for (register int i = (x); i >= (y); i--)
    #define full(x, y) memset(x, y, sizeof(x))
    #define lowbit(x) ((x) & (-x))
    const int maxn = 305;
    
    char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
    #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
    inline int read()
    {
        int x = 0, f = 1;
        char ch = getchar();
        while (!isdigit(ch))
        {
            if (ch == '-')
                f = -1;
            ch = getchar();
        }
        while (isdigit(ch))
        {
            x = x * 10 + ch - '0';
            ch = getchar();
        }
        return x * f;
    }
    
    int n, m, a[maxn], b[maxn], tree[maxn];
    
    void add(int x, int k)
    {
        for (; x <= m; x += lowbit(x))
            tree[x] += k;
    }
    
    int ask(int x)
    {
        int res = 0;
        for (; x; x -= lowbit(x))
            res += tree[x];
        return res;
    }
    
    int solve()
    {
        full(tree, 0);
        int res = 0;
        n = read(), m = read();
        rep(i, 1, m) a[i] = b[i] = read();
        std::sort(b + 1, b + m + 1);
        int nn = std::unique(b + 1, b + m + 1) - b - 1;
        rep(i, 1, m) a[i] = std::lower_bound(b + 1, b + nn + 1, a[i]) - b;
        rep(i, 1, m) add(a[i], 1), res += ask(a[i] - 1);
        return res;
    }
    
    int main(int argc, char const *argv[])
    {
        int T = read();
        while (T--)
            printf("%d\n", solve());
        return 0;
    }
    

    D2. Seating Arrangements (hard version)

    题目分析:与 \(easy\) \(version\) 不同的是现在有多行了,基本思路还是树状数组求正序对,一开始想的是先按数值排个序,然后按顺序每行选出 \(m\) 个人,再对这 \(m\) 个人按编号排序,用D1的做法求他们对答案的贡献,可惜超时了 ┑(﹀_﹀)┍

    后来去luogu逛了一圈发现有julao也用的是树状数组的思路,但julao的处理方式明显比我更优,这里开个旅行传送门,在此不过多赘述了。

    AC代码

    #include <bits/stdc++.h>
    #define rep(i, x, y) for (register int i = (x); i <= (y); i++)
    #define down(i, x, y) for (register int i = (x); i >= (y); i--)
    #define pii std::pair<int, int>
    #define full(x, y) memset(x, y, sizeof(x))
    #define lowbit(x) ((x) & (-x))
    const int maxn = 1e5 + 5;
    
    char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
    #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
    inline int read()
    {
        int x = 0, f = 1;
        char ch = getchar();
        while (!isdigit(ch))
        {
            if (ch == '-')
                f = -1;
            ch = getchar();
        }
        while (isdigit(ch))
        {
            x = x * 10 + ch - '0';
            ch = getchar();
        }
        return x * f;
    }
    
    int n, m, cnt;
    pii a[maxn], b[maxn];
    int c[maxn], tree[305][maxn];
    std::map<int, int> mp;
    
    void add(int x, int y, int k)
    {
        for (; x <= cnt; x += lowbit(x))
            tree[y][x] += k;
    }
    
    int ask(int x, int y)
    {
        int res = 0;
        for (; x; x -= lowbit(x))
            res += tree[y][x];
        return res;
    }
    
    inline bool cmp(pii x, pii y) { return x.second ^ y.second ? x.second < y.second : x.first < y.first; }
    
    int solve()
    {
        int res = 0;
        n = read(), m = read();
        rep(i, 1, n * m) a[i].second = b[i].second = c[i] = read(), b[i].first = i;
        std::sort(b + 1, b + n * m + 1, cmp);
        rep(i, 1, n * m) a[b[i].first].first = i;
        std::sort(c + 1, c + n * m + 1);
        cnt = std::unique(c + 1, c + n * m + 1) - c - 1;
        rep(i, 1, n * m) a[i].second = std::lower_bound(c + 1, c + cnt + 1, a[i].second) - c;
        rep(i, 1, n) rep(j, 1, cnt) tree[i][j] = 0;
        rep(i, 1, n * m)
        {
            int id = (a[i].first - 1) / m + 1;
            res += ask(a[i].second - 1, id);
            add(a[i].second, id, 1);
        }
        return res;
    }
    
    int main(int argc, char const *argv[])
    {
        int T = read();
        while (T--)
            printf("%d\n", solve());
        return 0;
    }
    

    E. Buds Re-hanging

    题意:给你一棵树,树的芽满足以下条件:

    • 非根非叶子节点
    • 所有子节点均为叶子节点

    你可以剪掉一棵芽(删去芽与其父节点的连边)并放在其他任意节点上,该操作可以进行任意次,现在要你最小化叶子节点数目,并求最小值是多少。

    题目分析:结论题,首先将整棵树尽可能多地拆成芽并放在根节点上形成一棵深度 \(\leq 3\) 的树,因为每个带有 \(x\) 个叶子节点的芽在嫁接到另一节点上后,对答案的贡献是 \(x-1\) ,所以保持其中一个芽不动,其它芽顺着它连成一条链即是最优解。

    图是从luogu偷的,但画的着实好,实在忍不住。

    E1.png

    为了观感更好,我在原图连成链的时候把嫁接的地方换成虚线了。

    E2.png

    AC代码

    #include <bits/stdc++.h>
    #define rep(i, x, y) for (register int i = (x); i <= (y); i++)
    #define down(i, x, y) for (register int i = (x); i >= (y); i--)
    const int maxn = 2e5 + 5;
    
    char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
    #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
    inline int read()
    {
        int x = 0, f = 1;
        char ch = getchar();
        while (!isdigit(ch))
        {
            if (ch == '-')
                f = -1;
            ch = getchar();
        }
        while (isdigit(ch))
        {
            x = x * 10 + ch - '0';
            ch = getchar();
        }
        return x * f;
    }
    
    int n, ans;
    std::vector<int> e[maxn];
    
    void init()
    {
        //其中一个芽不动,其对答案的贡献就为其叶节点数x而非x-1
        ans = 1;
        rep(i, 1, n) e[i].clear();
    }
    
    int dfs(int u, int f)
    {
        int cnt = 0;
        for (auto v : e[u])
        {
            if (v == f)
                continue;
            cnt += dfs(v, u);
        }
        if (cnt)
            ans += cnt - 1;
        return cnt ? 0 : 1;
    }
    
    int main(int argc, char const *argv[])
    {
        int T = read();
        while (T--)
        {
            n = read();
            init();
            rep(i, 1, n - 1)
            {
                int u = read(), v = read();
                e[u].push_back(v);
                e[v].push_back(u);
            }
            dfs(1, 0);
            printf("%d\n", ans);
        }
        return 0;
    }
    
  • 相关阅读:
    ffmpeg mp4 视频输出为 aac 的命令
    git操作远程分支
    Linux (openSUSE 15.3 ) server ssh 可以使用,sftp无法使用
    小小思考题
    Linux 命令介绍
    2021 年 如何使用VMware 安装 ubuntu 7.04 虚拟机, 配置 apt 源
    Linux 下 命令行 使用浏览器
    oracle.cmd
    Sqlcmd
    vue eslint 配置使用
  • 原文地址:https://www.cnblogs.com/Foreign/p/15321905.html
Copyright © 2011-2022 走看看