[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 = '\0';
}
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\n",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 = '\0';
}
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('\n');
    }
    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 = '\0';
}
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\n",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 = '\0';
}
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\n",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 = '\0';
}
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\n",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\n",v,in[v]);
        }
    }
    //    for(int i = 1;i<=n;++i) printf("%d %d %d\n",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\n", E[i].from, E[i].to);
            else if (id[E[i].from] < id[E[i].to]) printf("%d %d\n", E[i].from, E[i].to);
            else printf("%d %d\n", 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 = '\0';
}
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\n",ans);
    }
    return 0;
}
posted @ 2020-07-18 22:31  *Clouder  阅读(241)  评论(0编辑  收藏  举报