zoukankan      html  css  js  c++  java
  • [Codeforces]Round 656 div3 题解(A-E)

    Before the Beginning

    转载请将本段放在文章开头显眼处,如有二次创作请标明。
    原文链接:https://www.codein.icu/cf1385/

    前言

    放假后的第一场比赛,深夜场。
    Div3开小号打了,开始状态奇差,后来还可以,A-E都一发过了,没罚多少时,但开局不利,Rank没进500.
    整体有一定的思维难度,质量不错的题目。

    A

    这次的 A 题做出人数比 B 题还少,卡了我20min……
    给出(x = max(a,b))(y = max(a,c))(z = max(b,c)) 要求找到任意一组符合条件的 (a,b,c)

    一开始想分类讨论没想清楚,后来官方发了个 Announcement 说输出顺序任意,就想到怎么打了。

    首先可以确定最大的数,确定最大的数后,最大的数一定出现两次。

    那么剩下那个数就是第二大的数,最小的数取 (1) 即可。

    #include <cstdio>
    #include <algorithm>
    #include <ctype.h>
    const int bufSize = 1e6;
    inline char nc()
    {
        #ifdef DEBUG
        return getchar();
        #endif
        static char buf[bufSize], *p1 = buf, *p2 = buf;
        return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, bufSize, stdin), p1 == p2) ? EOF : *p1++;
    }
    inline void read(char *s)
    {
        static char c;
        for (; !isalpha(c); c = nc());
        for (; isalpha(c); c = nc()) *s++ = c;
        *s = '';
    }
    template<typename T>
    inline T read(T &r)
    {
        static char c;
        static int flag;
        flag = 1, r = 0;
        for (c = nc(); !isdigit(c); c = nc()) if (c == '-') flag = -1;
        for (; isdigit(c); c = nc()) r = r * 10 + c - 48;
        return r *= flag;
    }
    int T;
    int a[4];
    int main()
    {
        read(T);
        while(T--)
        {
            for(int i = 1;i<=3;++i) read(a[i]);
            std::sort(a + 1,a + 4);
            int maxx = a[3];
            if(a[2] != a[3])
            {
                puts("NO");
                goto end;
            }
            puts("YES");
            printf("%d %d %d
    ",maxx,a[1],1);
            end:;
        }
        return 0;
    }
    

    B

    B 题的数据范围看上去似乎想让人用暴力,但其实是个 (O(n)) 的结论题。

    观察样例发现,只取序列中首次出现的数,就能还原数组。现在我们证明这个结论:

    第一个数一定是原数组的第一个数。

    将数组中所有出现的第一个数删除后,原先偏序不变。

    随后问题即缩小成子问题,依次求解。

    删除过程等价于打个值标记,每次取第一个数其实就是顺序遍历。

    #include <cstdio>
    #include <algorithm>
    #include <ctype.h>
    const int bufSize = 1e6;
    inline char nc()
    {
        #ifdef DEBUG
        return getchar();
        #endif
        static char buf[bufSize], *p1 = buf, *p2 = buf;
        return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, bufSize, stdin), p1 == p2) ? EOF : *p1++;
    }
    inline void read(char *s)
    {
        static char c;
        for (; !isalpha(c); c = nc());
        for (; isalpha(c); c = nc()) *s++ = c;
        *s = '';
    }
    template<typename T>
    inline T read(T &r)
    {
        static char c;
        static int flag;
        flag = 1, r = 0;
        for (c = nc(); !isdigit(c); c = nc()) if (c == '-') flag = -1;
        for (; isdigit(c); c = nc()) r = r * 10 + c - 48;
        return r *= flag;
    }
    int T,n;
    const int maxn = 120;
    int a[maxn],vis[maxn];
    int main()
    {
        read(T);
        while(T--)
        {
            read(n);
            for(int i = 1;i<=n;++i) vis[i] = 0;
            for (int i = 1; i <= n * 2; ++i)
            {
                int x;
                read(x);
                if(!vis[x]) vis[x] = 1,printf("%d ",x);
            }
            putchar('
    ');
        }
        return 0;
    }
    

    c

    首先考虑 (b) 数组的性质,要求每次取头尾元素,所取元素单调不减。

    那么一定存在一个峰点,峰点左边元素单调不减,峰点右边元素单调不增。

    否则,如果存在双峰点,那么某个峰点内侧的元素就会比其小,从而不满足要求。

    那么解法就简单了,从后往前找到第一个峰点,从峰点向前找到第二个峰点,删除第二个峰点即其左边的元素即可。

    #include <cstdio>
    #include <algorithm>
    #include <ctype.h>
    const int bufSize = 1e6;
    inline char nc()
    {
        #ifdef DEBUG
        return getchar();
        #endif
        static char buf[bufSize], *p1 = buf, *p2 = buf;
        return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, bufSize, stdin), p1 == p2) ? EOF : *p1++;
    }
    inline void read(char *s)
    {
        static char c;
        for (; !isalpha(c); c = nc());
        for (; isalpha(c); c = nc()) *s++ = c;
        *s = '';
    }
    template<typename T>
    inline T read(T &r)
    {
        static char c;
        static int flag;
        flag = 1, r = 0;
        for (c = nc(); !isdigit(c); c = nc()) if (c == '-') flag = -1;
        for (; isdigit(c); c = nc()) r = r * 10 + c - 48;
        return r *= flag;
    }
    const int maxn = 2e5 + 100;
    int T,n;
    int a[maxn],f[maxn];
    int front[maxn],back[maxn];
    int main()
    {
        read(T);
        while(T--)
        {
            read(n);
            for (int i = 1; i <= n; ++i) read(a[i]);
            int top = n;
            while(top > 1 && a[top - 1] >= a[top]) --top;
            int head = top;
            while(head > 1&& a[head - 1] <= a[head]) --head;
            printf("%d
    ",head - 1);
        }
        return 0;
    }
    

    D

    阅读题面,发现好串的定义就是在提示使用分治做法。

    每次可以假设左边全是 (c) 或右边全是 (c),计算相应的代价,取最小值即可。

    这样处理过程就像遍历一棵线段树一样,复杂度 (O(2n))

    计算代价可以用前缀和快速处理。

    #include <cstdio>
    #include <algorithm>
    #include <ctype.h>
    #define int long long
    const int bufSize = 1e6;
    inline char nc()
    {
        #ifdef DEBUG
        return getchar();
        #endif
        static char buf[bufSize], *p1 = buf, *p2 = buf;
        return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, bufSize, stdin), p1 == p2) ? EOF : *p1++;
    }
    inline void read(char *s)
    {
        static char c;
        for (; !isalpha(c); c = nc());
        for (; isalpha(c); c = nc()) *s++ = c;
        *s = '';
    }
    template<typename T>
    inline T read(T &r)
    {
        static char c;
        static int flag;
        flag = 1, r = 0;
        for (c = nc(); !isdigit(c); c = nc()) if (c == '-') flag = -1;
        for (; isdigit(c); c = nc()) r = r * 10 + c - 48;
        return r *= flag;
    }
    const int maxn = 2e5 + 100;
    char s[maxn];
    int sum[maxn][26];
    int T,n;
    inline int solve(int l,int r,int c)
    {
        if(l == r) return c != s[l];
        int mid = l + r >> 1;
        int left = solve(l,mid,c + 1),right = solve(mid + 1,r,c + 1);
        int lval = left + r - mid - (sum[r][c] - sum[mid][c]);
        int rval = right + (mid - l + 1) - (sum[mid][c] - sum[l - 1][c]);
        return std::min(lval,rval);
    }
    signed main()
    {
        read(T);
        while(T--)
        {
            read(n);
            read(s + 1);
            for(int i = 1;i<=n;++i) s[i] -= 'a';
            for (int i = 1; i <= n; ++i) 
            {
                for (int j = 0; j < 26; ++j) sum[i][j] = sum[i - 1][j];
                sum[i][s[i]]++;
            }
            printf("%lld
    ",solve(1,n,0));
        }
        return 0;
    }
    

    E

    给定一个有向图,给一些无向边,要为无向边决定方向,使最终图是个有向无环图。

    一开始想在原图上乱搜一通,然后试错法乱选方向,样例都没过去……

    但可以发现,无论如何,都是从给定的有向图的性质入手。

    如果原图含有环,一定是 NO,否则一定是 YES。

    有向无环图才有拓扑排序,而如果最终图有拓扑排序,它就是有向无环图。

    对原图进行拓扑排序,顺便判环。

    对于每条无向边,按照拓扑排序的顺序决定方向即可。

    代码写得乱了点。

    #include <cstdio>
    #include <algorithm>
    #include <ctype.h>
    const int bufSize = 1e6;
    inline char nc()
    {
    #ifdef DEBUG
        return getchar();
    #endif
        static char buf[bufSize], *p1 = buf, *p2 = buf;
        return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, bufSize, stdin), p1 == p2) ? EOF : *p1++;
    }
    inline void read(char *s)
    {
        static char c;
        for (; !isalpha(c); c = nc());
        for (; isalpha(c); c = nc()) *s++ = c;
        *s = '';
    }
    template <typename T>
    inline T read(T &r)
    {
        static char c;
        static int flag;
        flag = 1, r = 0;
        for (c = nc(); !isdigit(c); c = nc()) if (c == '-') flag = -1;
        for (; isdigit(c); c = nc()) r = r * 10 + c - 48;
        return r *= flag;
    }
    const int maxn = 2e5 + 100, maxm = maxn << 1;
    struct node
    {
        int from, to, next, directed;
    } E[maxm];
    int head[maxn], tot;
    inline void add(const int &x, const int &y, const int &t)
    {
        E[++tot].next = head[x], E[tot].from = x, E[tot].to = y, E[tot].directed = t, head[x] = tot;
    }
    int T, n, m;
    int in[maxn];
    int q[maxn], qh, qt, id[maxn], vis[maxn], cnt, ans;
    inline void topo()
    {
        qh = 1, qt = 0;
        for (int i = 1; i <= n; ++i) if (!in[i]) q[++qt] = i;
        while (qt >= qh)
        {
            if(ans) return;
            int u = q[qh++];
            id[u] = ++cnt, vis[u]++;
            //        printf("inq:%d
    ",u);
            for (int p = head[u]; p; p = E[p].next)
            {
                if (!E[p].directed) continue;
                int v = E[p].to;
                if (--in[v] == 0) q[++qt] = v;
                if (in[v] < 0) ans = 1;
                //            printf("in:%d %d
    ",v,in[v]);
            }
        }
        //    for(int i = 1;i<=n;++i) printf("%d %d %d
    ",i,id[i],vis[i]);
    }
    int main()
    {
        read(T);
        while (T--)
        {
            ans = tot = cnt = 0;
            read(n), read(m);
            for (int i = 1; i <= n; ++i)
                vis[i] = head[i] = 0, in[i] = 0;
            for (int i = 1; i <= m; ++i)
            {
                int t, a, b;
                read(t), read(a), read(b);
                add(a, b, t);
                if (t)
                    in[b]++;
            }
            topo();
            if(ans) {puts("NO"); goto end;}
            for (int i = 1; i <= n; ++i) if (!id[i] || vis[i] != 1){puts("NO");goto end;}
            puts("YES");
            for (int i = 1; i <= m; ++i)
            {
                if (E[i].directed) printf("%d %d
    ", E[i].from, E[i].to);
                else if (id[E[i].from] < id[E[i].to]) printf("%d %d
    ", E[i].from, E[i].to);
                else printf("%d %d
    ", E[i].to, E[i].from);
            }
            continue;
        end:;
        }
        return 0;
    }
    

    F

    这题赛中没做出来,现在也没来得及补正解,随便口胡两句。

    该解法是错误的,第二个pretest就WA了QAQ

    主要原因是看错题了。要求删除的叶子都在同一点上,我以为是任选……

    以下解法看看就好,愚蠢的代码也贴上来吧。

    每次删除恰好 (k) 个叶子,很容易让人想到贪心。

    如果一个节点恰好与 (x) 个叶子相连,那么删除这 (x) 个叶子后它将成为叶子。将这种即为一类点。

    将所有一类点放入堆中,按 (x) 升序排序。

    也将所有与叶子相连的、不全与叶子相连的点放入堆中,优先级最低,即放在尾部。将这类点记为二类点。

    选取 (k) 个叶子时,从堆顶取出:

    如果 (x leq k),那么该节点会成为叶子,检查它的父亲是否能成为一类点,如果可以放入堆中,否则作为二类点放入堆中。

    如果 (x > k),那么该节点会取部分,且当前次取叶子成功。更新在堆中的状态。

    用配对堆来维护即可。

    #include <cstdio>
    #include <ext/pb_ds/priority_queue.hpp>
    #include <algorithm>
    #include <ctype.h>
    const int bufSize = 1e6;
    using namespace std;
    using namespace __gnu_pbds;
    inline char nc()
    {
        #ifdef DEBUG
        return getchar();
        #endif
        static char buf[bufSize], *p1 = buf, *p2 = buf;
        return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, bufSize, stdin), p1 == p2) ? EOF : *p1++;
    }
    inline void read(char *s)
    {
        static char c;
        for (; !isalpha(c); c = nc());
        for (; isalpha(c); c = nc()) *s++ = c;
        *s = '';
    }
    template<typename T>
    inline T read(T &r)
    {
        static char c;
        static int flag;
        flag = 1, r = 0;
        for (c = nc(); !isdigit(c); c = nc()) if (c == '-') flag = -1;
        for (; isdigit(c); c = nc()) r = r * 10 + c - 48;
        return r *= flag;
    }
    const int maxn = 2e5 + 100;
    int fa[maxn],sonnum[maxn],leafnum[maxn];
    struct node
    {
        int x,id,type;
        bool operator<(const node &that) const
        {
            if(this->type != that.type) return this->type > that.type;
            return leafnum[id] > leafnum[that.id];
        }
    };
    int T,n,k;
    struct edge
    {
        int to,next;
    }E[maxn << 1];
    int head[maxn],tot;
    inline void add(const int &x,const int &y)
    {
        E[++tot].next = head[x], head[x] = tot, E[tot].to = y;
    }
    __gnu_pbds::priority_queue<node,less<node>,pairing_heap_tag> q;
    __gnu_pbds::priority_queue<node,less<node>,pairing_heap_tag>::point_iterator vis[maxn];
    void dfs(int u)
    {
        if (!head[u]) leafnum[fa[u]]++;
        for (int p = head[u]; p; p = E[p].next)
        {
            int v = E[p].to;
            if(v == fa[u]) continue;
            fa[v] = u,dfs(v);
            sonnum[u]++;
        }
        if(sonnum[u] == leafnum[u])
        {
            node t;
            t.x = leafnum[u],t.id = u,t.type = 0;
            vis[u] = q.push(t);
        }
        else if(leafnum[u]) 
        {
            node t;
            t.x = leafnum[u],t.id = u,t.type = 1;
            vis[u] = q.push(t);
        }
    }
    inline bool take(int now)
    {
        while(!q.empty())
        {
            node u = q.top();
            if(now >= u.x)
            {
                //拿光当前点情况
                q.pop();
                now -= u.x;
                if(!u.type)
                {
                    sonnum[u.id] = leafnum[u.id] = 0;
                    int v = fa[u.id];
                    leafnum[v]++;
                    if(leafnum[v] == sonnum[v])
                    {
                        node t;
                        t.x = leafnum[v],t.type = 0,t.id = v;
                        if(vis[v] == 0) vis[v] = q.push(t);
                        else q.modify(vis[v],t);
                    }
                    else if(leafnum[v] < sonnum[v])
                    {
                        node t;
                        t.x = leafnum[v],t.type = 1,t.id = v;
                        if(vis[v] == 0) vis[v] = q.push(t);
                        else q.modify(vis[v],t);
                    }
                }
                else sonnum[u.id] -= u.x, leafnum[u.id] -= u.x;
                if(now == 0) return true;
            }
            else
            {
                //部分取走情况
                leafnum[u.id] -= now, sonnum[u.id] -= now;
                node t;
                t.id = u.id, t.x = leafnum[u.id], t.type = u.type;
                if(vis[u.id] == 0) vis[u.id] = q.push(t);
                else q.modify(vis[u.id], t);
                return true;
            }
        }
        return false;
    }
    int main()
    {
        read(T);
        while(T--)
        {
            tot = 0;
            read(n), read(k);
            for(int i = 1;i<=n;++i) vis[i] = 0,head[i] = 0,sonnum[i] = leafnum[i] = 0,fa[i] = 0;
            while(!q.empty()) q.pop();
            for (int i = 1; i < n; ++i)
            {
                int a, b;
                read(a), read(b);
                add(a, b), add(b, a);
            }
            dfs(1);
            int ans = 0;
            while(take(k)) ++ans;
            printf("%d
    ",ans);
        }
        return 0;
    }
    
  • 相关阅读:
    野心和实力的磨合
    tpm
    菜猫学习linux笔记(1)
    调试理解过程
    TSS学习记录
    RSA加密算法理解(整理自网络)
    *args和**kwargs在python中的作用
    在 Ubuntu 16.04 中安装谷歌 Chrome 浏览器
    ubuntu安装微信客户端
    ubuntu 桌面操作系统安装WPS办公软件的方法
  • 原文地址:https://www.cnblogs.com/Clouder-Blog/p/cf1385.html
Copyright © 2011-2022 走看看