牛客小白月赛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;
}
原文地址:https://www.cnblogs.com/grain-rain/p/14818106.html