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;
    }
    
  • 相关阅读:
    在IE和Firfox获取keycode
    using global variable in android extends application
    using Broadcast Receivers to listen outgoing call in android note
    help me!virtual keyboard issue
    using iscroll.js and iscroll jquery plugin in android webview to scroll div and ajax load data.
    javascript:jquery.history.js使用方法
    【CSS核心概念】弹性盒子布局
    【Canvas学习笔记】基础篇(二)
    【JS核心概念】数据类型以及判断方法
    【问题记录】ElementUI上传组件使用beforeupload钩子校验失败时的问题处理
  • 原文地址:https://www.cnblogs.com/Clouder-Blog/p/cf1385.html
Copyright © 2011-2022 走看看