• 牛客小白月赛29全题解


    牛客小白月赛29全题解

    A.进攻

    题目链接:https://ac.nowcoder.com/acm/contest/8564/A

    思路:

    1. 将基地按防御值为第一关键字,价值为第二关键字从小到大排序。将战机按破坏力从小到大排序。
    2. 由于战机能破坏任意防御值比其破坏力小的基地,所以选择破坏防御值比其破坏力小的价值最大的基地即可
    3. 有2可以导出,先预处理出每个基地的价值的前缀最大值,这个最大值就是可以破坏该基地的战机所能获得的最大价值
    4. 从大到小扫遍历机和基地,将可以摧毁的且价值大于0的价值累加即为答案

    注意:1、价值可能为负数;2、在遍历的时候,在战机遍历完之前,基地可能已经遍历完了

    由于第二条,卡了近一个小时,wa了无数发。。。

    AC代码:

    #include <iostream>
    #include <algorithm>
    
    #define x first
    #define y second
    
    using namespace std;
    
    typedef pair<int, int> PII;
    
    const int N = 1e6+10;
    
    int n, m, a[N];
    PII dv[N];
    
    int main()
    {
        cin >> n >> m;
        for(int i = 0; i < n; i++) scanf("%d", &a[i]);
        for(int i = 0; i < m; i++) scanf("%d", &dv[i].x);
        for(int i = 0; i < m; i++) scanf("%d", &dv[i].y);
        sort(a, a + n);
        sort(dv, dv + m);
        int mv = -1e3 - 10;
        for(int i = 0; i < m; i++)
        {
            mv = max(mv, dv[i].y);
            dv[i].y = mv;
        }
        
        int ans = 0;
        for(int i = n - 1, j = m - 1; ~i && ~j;)
        {
            if(a[i] > dv[j].x)
            {
                if(dv[j].y > 0) ans += dv[j].y;
                else break;
                i--;
            }
            else j--;
        }
        cout << ans << endl;
        
        return 0;
    }
    

    B.二进制

    题目链接:https://ac.nowcoder.com/acm/contest/8564/B

    可以确定,一定可以通过三步与、或、异或得到最后的结果。

    观察01与、或、异或的值:

    与;            或:            异或:
    0 & 1 = 0      0 | 1 = 1       0 ^ 1 = 1
    1 & 1 = 1      1 | 1 = 1       1 ^ 1 = 0
    0 & 0 = 0      0 | 0 = 0       0 ^ 0 = 0
    1 & 0 = 0      1 | 0 = 1       1 ^ 0 = 1
    

    可以发现

    1. 0/1 & 1值不变0/1 | 0值不变0/1 ^ 0值不变
    2. 开始为0,结果为0;开始为1, 结果为0的情况是&0
    3. 开始为0,结果为1;开始为1, 结果为1的情况是|1
    4. 开始为0,结果为1;开始为1, 结果为0的情况是^0

    所以我们可以以0和220-1为初始值,然后经过所有操作后得结果,一位一位比较前后值的变化,依次推断出该位是&0还是|1还是^1

    AC代码:

    #include <iostream>
    #include <bitset>
    
    using namespace std;
    
    int n;
    bitset<20> ans1((1 << 20) - 1), ans2(0), ans3(0);
    
    int main()
    {
        cin >> n;
        int x1 = 0, x2 = (1 << 20) - 1;
        while(n--)
        {
            int op, a;
            scanf("%d%d", &op, &a);
            if(op == 1) x1 = x1 & a, x2 = x2 & a;
            else if(op == 2) x1 = x1 | a, x2 = x2 | a;
            else x1 = x1 ^ a, x2 = x2 ^ a;
        }
        cout << 3 << endl;
        for(int i = 0, j = 0; i < 20; i++, j++)
        {
            if(((x1 >> i) & 1) == 0 && ((x2 >> i) & 1) == 0) 
                ans1[j] = 0;
            else if(((x1 >> i) & 1) == 1 && ((x2 >> i) & 1) == 1) ans2[j] = 1;
            else if(((x1 >> i) & 1) == 1 && ((x2 >> i) & 1) == 0) ans3[j] = 1;
        }
    
        cout << 1 << ' ' << ans1.to_ulong() << endl;
        cout << 2 << ' ' << ans2.to_ulong() << endl;
        cout << 3 << ' ' << ans3.to_ulong() << endl;
    
        return 0;
    }
    

    C.积木

    题目链接:https://ac.nowcoder.com/acm/contest/8564/C

    构造题

    参考题解:积木_欢迎您的光顾awa-CSDN博客

    AC代码:

    /*
    采用构造方法:
    00 11
    00 11
    交替出现
    */
    #include <iostream>
    
    using namespace std;
    
    int main()
    {
        int n;
        cin >> n;
        if(n & 1)
        {
            puts("-1");
            return 0;
        }
        
        for(int h = 1; h <= n; h++)
        {
            for(int k = ((h & 1) ? 0 : 1), i = 0; i < n; i += 2, k = (k + 1) % 2)
            {
                for(int x = k, j = 0; j < n; j += 2)
                {
                    printf("%d %d ", x, x);
                    x = (x + 1) % 2;
                }
                putchar('
    ');
                for(int x = k, j = 0; j < n; j += 2)
                {
                    printf("%d %d ", x, x);
                    x = (x + 1) % 2;
                }
                putchar('
    ');
            }
        }
        
        return 0;
    }
    

    D.种树

    题目链接:https://ac.nowcoder.com/acm/contest/8564/D

    根据题目要求可得,需要剪枝的次数(m = (n - 1) / 2),所以用大剪刀的次数为(ma = (m + 1) / 2)

    分析可得:如果我们在最开始就用大剪刀的话,后面用小剪刀会把最大值替换掉,也就是说先用大剪刀会被后面的小剪刀抵消掉。

    由此可的:从深度为ma的那一层点开始,到根节点全部用大剪刀,深度大于ma的全部用小剪刀。根据题意遍历完所有节点,同时更新。最后根节点的生命力即为结果。

    AC代码:

    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 5e5+10;
    
    int n, m, ma, l, r, v, root;
    bool st[N];
    struct Node
    {
        int l, r, v;
        bool yz;
    }tr[N];
    
    void dfs(int root, int h)
    {
        int ls = tr[root].l, rs = tr[root].r;
        if(ls) dfs(ls, h + 1);
        if(rs) dfs(rs, h + 1);
        if(tr[ls].yz && tr[rs].yz)
        {
            if(h >= ma) tr[root].v = min(tr[ls].v, tr[rs].v);
            else tr[root].v = max(tr[ls].v, tr[rs].v);
        }
        tr[root].l = tr[root].r = 0, tr[root].yz = true;
    }
    
    int main()
    {
        cin >> n;
        m = n / 2;
        ma = (m + 1) / 2;
        for(int i = 1; i <= n; i++)
        {
            scanf("%d%d", &l, &r);
            tr[i].l = l, tr[i].r = r, tr[i].yz = !(l && r);
            st[l] = st[r] = true;
        }
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &v);
            tr[i].v = v;
        }
        //找根节点
        for(int i = 1; i <= n; i++)
            if(!st[i])
            {
                root = i;
                break;
            }
        dfs(root, 0);
        cout << tr[root].v << endl;
        
        return 0;
    }
    

    E.考试

    题目链接:https://ac.nowcoder.com/acm/contest/8564/E

    首先遍历,计算出两人答案相同的题目数量t

    • (t(相同数量)<= n- k(答案正确的数量))时,相同的全对,不同的自己正确,ans = t + k
    • (t(相同数量)> n- k(答案正确的数量)),相同部分全对,剩余的相同部分全错,ans = n - (t - (n - k))

    AC代码:

    #include <iostream>
    
    using namespace std;
    
    const int N = 1010;
    
    int n, k;
    int a[N], t;
    
    int main()
    {
        cin >> n >> k;
        for(int i = 0; i < n; i++) scanf("%d", &a[i]);
        for(int i = 0; i < n; i++)
        {
            int x;
            scanf("%d", &x);
            if(x == a[i]) t++;
        }
        
        if(t <= n - k) cout << t + k << endl;
        else cout << n - (t - (n - k)) << endl;
        return 0;
    }
    

    F.项链

    题目链接:https://ac.nowcoder.com/acm/contest/8564/F

    写一个循环双链表即可,翻转就是l[]和r[]交换

    AC代码:

    #include <iostream>
    
    using namespace std;
    
    const int N = 2e5+10;
    
    int n, m, op, x, y;
    int e[N], l[N], r[N], idx, p[N], t[N];
    
    void insert_r(int a, int x)
    {
        e[idx] = x, p[x] = idx;
        l[idx] = a, r[idx] = r[a];
        l[r[a]] = idx, r[a] = idx++;
    }
    
    void insert_l(int a, int x)
    {
        e[idx] = x, p[x] = idx;
        r[idx] = a, l[idx] = l[a];
        r[l[a]] = idx, l[a] = idx++;
    }
    
    void remove(int a)
    {
        l[r[a]] = l[a];
        r[l[a]] = r[a];
    }
    
    void init(int n)
    {
        r[0] = 1, l[0] = 1;
        r[1] = 0, l[1] = 0;
        idx = 2;
        for(int i = 1; i <= n; i++) insert_r(idx - 1, i);
    }
    
    int main()
    {
        cin >> n >> m;
        init(n);
        while(m--)
        {
            scanf("%d", &op);
            if(op == 1 || op == 2) scanf("%d%d", &x, &y);
            
            if(op == 1)
            {
                remove(p[x]);
                insert_r(p[y], x);
            }
            else if(op == 2)
            {
                remove(p[x]);
                insert_l(p[y], x);
            }
            else if(op == 3)
            {
                for(int i = 0; i < idx; i++)
                {
                    int t = l[i];
                    l[i] = r[i], r[i] = t;
                }
            }
            else
            {
                for(int i = p[1], j = 1; j <= n; i = r[i], j++)
                {
                    if(i == 1 || !i)
                    {
                        j--;
                        continue;
                    }
                    if(j < n) printf("%d ", e[i]);
                    else printf("%d
    ", e[i]);
                }
            }
        }
        
        return 0;
    }
    

    G.涂色

    题目链接:https://ac.nowcoder.com/acm/contest/8564/G

    由于只能填0、1并且不能出现10,则可推出:按顺序填,一旦填了一个1,则后面只能是1。

    所以,1可以出现的位置数量n(每个位置可出现,且各不相同同)+1(全0的情况)即为答案。

    AC代码:

    #include <iostream>
    
    using namespace std;
    
    int main()
    {
        int n;
        cin >> n;
        cout << n + 1 << endl;
        return 0;
    }
    

    H.圆

    题目链接:https://ac.nowcoder.com/acm/contest/8564/H

    直接判断即可:

    圆心的距离(d = sqrt{(x1 - x2)^2 + (y1 - y2)^2})

    • (d > r1 + r2quad(d^2 > (r1 + r2)^2))或者(d < |r1 - r2|quad(d^2 < (r1 - r2)^2))时,无交点
    • 否则有交点

    AC代码:

    #include <iostream>
    
    using namespace std;
    
    typedef long long LL;
    
    int t;
    LL x1, y1, r1, x2, y2, r2;
    
    int main()
    {
        cin >> t;
        while(t--)
        {
            cin >> x1 >> y1 >> r1 >> x2 >> y2 >> r2;
            LL d = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
            if(d > (r1 + r2) * (r1 + r2) || d <  (r1 - r2) * (r1 - r2)) puts("NO");
            else puts("YES");
        }
        
        return 0;
    }
    

    I.修改

    题目链接:https://ac.nowcoder.com/acm/contest/8564/I

    参考题解:牛客小白月赛29 - cumtljz - 博客园 (cnblogs.com)

    要使数组所有数都变为0,则其差分数组都变为0。对于一个操作[l,r,w],可使差分数组第l位变为0,因此,如果所有位置都能与第n+1位联通,那么就存在一些操作让差分数组都变成0。那么如果将每个操作[l,r,w]视为一条边,求最小生成树即为最小解(注意:对原数组区间操作,影响的是差分数组的l和r+1位)。

    AC代码:

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    
    using namespace std;
    
    typedef long long LL;
    
    const int N = 2e5+10, INF = 0x3f3f3f3f;
    
    int n, m, l, r, w;
    int p[N];
    struct Edge
    {
        int l, r, w;
        
        bool operator < (const Edge &W) const
        {
            return w < W.w;
        }
    }edges[N];
    
    
    int find(int x)
    {
        if(p[x] != x) p[x] = find(p[x]);
        return p[x];
    }
    
    LL kruskal()
    {
        sort(edges, edges + m);
        for(int i = 1; i <= n + 1; i++) p[i] = i;
        
        LL res = 0, cnt = 0;
        for(int i = 0; i < m; i++)
        {
            int a = edges[i].l, b = edges[i].r, w = edges[i].w;
            a = find(a), b = find(b + 1);
            if(a != b)
            {
                res += w;
                cnt++;
                p[a] = b;
            }
        }
        
        if(cnt < n - 1) return INF;
        else return res;
    }
    
    int main()
    {
        cin >> n >> m;
        for(int i = 0; i < m; i++)
        {
            scanf("%d%d%d", &l, &r, &w);
            edges[i] = {l, r, w};
        }
        
        LL t = kruskal();
        if(t == INF) puts("-1");
        else cout << t << endl;
        return 0;
    }
    

    J.克隆

    题目链接:https://ac.nowcoder.com/acm/contest/8564/J

    参考题解:牛客小白月赛29 - cumtljz - 博客园 (cnblogs.com)

    关于DFS序和欧拉序dfs序和欧拉序 - Styx-ferryman - 博客园 (cnblogs.com)

    AC代码:

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    
    using namespace std;
    
    const int N = 1e5+10;
    
    int n, m, k, ma;
    int h[N], e[4 * N], ne[4 * N], idx;
    int p[4 * N], cnt;
    bool st[N];
    
    void add(int a, int b)
    {
        e[idx] = b;
        ne[idx] = h[a];
        h[a] = idx++;
    }
    
    void dfs(int u)
    {
        st[u] = true;
        p[cnt++] = u;
        for(int i = h[u]; ~i; i = ne[i])
        {
            int j = e[i];
            if(st[j]) continue;
            dfs(j);
            p[cnt++] = u;
        }
    }
    
    int main()
    {
        cin >> n >> m >> k;
        ma = (2 * n + k - 1) / k;
        memset(h, -1, sizeof h);
        for(int i = 0; i < m; i++)
        {
            int u, v;
            scanf("%d%d", &u, &v);
            add(u, v), add(v, u);
        }
        dfs(1);
        
        puts("YES");
        int num = cnt / ma, k = 0;
        for(int i = 0; i < num; i++)
        {
            printf("%d", ma);
            for(int j = 0; j < ma; j++)
                printf(" %d", p[k++]);
            putchar('
    ');
        }
        
        if(cnt % ma)
        {
            printf("%d", cnt % ma);
            for(; k < cnt; k++) printf(" %d", p[k]);
            putchar('
    ');
        }
        
        return 0;
    }
    
  • 相关阅读:
    20181123(编码问题)
    20181122笔记(列表、元组、字典和集合的数据类型及内置方法)
    20181121笔记(for,数字类型和字符串类型的内置方法)
    20181120笔记
    Python的第3堂课
    错误集合(想起来就更新)
    Python的第二堂课(2)
    Python的第二堂课(1)
    boost::bind实践2——来自《Beyond the C++ Standard Library ( An Introduction to Boost )》
    boost::bind实践
  • 原文地址:https://www.cnblogs.com/grain-rain/p/14818106.html
Copyright © 2020-2023  润新知