Codeforces Round #796 (Div. 2)(A~E题题解)

原题链接:

#Name
A Cirno’s Perfect Bitmasks Classroom standard input/output 1 s, 256 MBSubmit Add to favouritesx16108
B Patchouli’s Magical Talisman standard input/output 1 s, 256 MB[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sPY377qf-1656690657896)(https://cdn.codeforces.com/s/76331/images/icons/submit-22x22.png)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MXUQ8a22-1656690657897)(https://cdn.codeforces.com/s/76331/images/icons/star_gray_16.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fOBFqXu0-1656690657897)(https://cdn.codeforces.com/s/76331/images/icons/user.png)] x14942
C Manipulating History standard input/output 1 s, 256 MB[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A72piHxG-1656690657897)(https://cdn.codeforces.com/s/76331/images/icons/submit-22x22.png)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i34UN98a-1656690657898)(https://cdn.codeforces.com/s/76331/images/icons/star_gray_16.png)]img x5823
D The Enchanted Forest standard input/output 2 s, 256 MB[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f6oRME45-1656690657899)(https://cdn.codeforces.com/s/76331/images/icons/submit-22x22.png)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NIF8Xz4Q-1656690657899)(https://cdn.codeforces.com/s/76331/images/icons/star_gray_16.png)]img x5066
E Railway System standard input/output 1 s, 256 MBSubmitimg x1056
F Sanae and Giant Robot standard input/output 1 s, 256 MB[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nrJjKx6x-1656690657901)(https://cdn.codeforces.com/s/76331/images/icons/submit-22x22.png)] Add to favourites[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TkM5E1b7-1656690657902)(https://cdn.codeforces.com/s/76331/images/icons/user.png)] x186

A.Cirno’s Perfect Bitmasks Classroom

在这里插入图片描述

思路

对于x,如果x里面含有两个1,那么最小答案就是x的最低一位1所构成的数字。(最低一位and可以是非零,高位与另一个1异或,也是非零)。
如果要是x只有一个1,那么所产生的数字在这一位要有一个1,并且在最低的0位还要有一个1这样,才能保证最后的数字非零。

代码

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        int ans;
        int x;
        cin >> x;
        int low1;
        int low0;
        int cnt1;
        low1 = low0 = -1;
        cnt1 = 0;
        bool fir1 = false;
        bool fir0 = false;
        for(int i = 0; i <= 31; i++)
        {
            int t = (x>>i) & 1;
            if(!t && !fir0)
            {
                fir0 = true;
                low0 = i;
            }
            if(t)
            {
                cnt1 ++;
                if(!fir1)
                {
                    low1 = i;
                    fir1 = true;
                }
            }
        }
        if(cnt1 > 1)
        {
            ans = (1<<low1);
        }
        else
        {
            ans = (1 << low1) + (1 << low0);
        }
        printf("%d\n", ans);
    }
    return 0;
}

B.Patchouli’s Magical Talisman

在这里插入图片描述

思路

使用的H数组,可以较快地求出最低的1是在哪一位。
H数组的大小稍微大一点没有问题。但是最后必须要mod37.可以处理的范围是0到(1<<35)%37.
在题目中,对于一个偶数,有两种操作:

  1. 把这个偶数除以2,但是可能会有偶数经过一次操作不能变成奇数,例如8
  2. 把偶数和奇数合在一起,这样就只需要1次就可以消灭一个偶数。

所以有贪心策略。
对于一个数列,如果存在奇数,那么最终所需要的操作次数就是所有偶数的个数之和(全部和奇数进行合并)
如果要是,没有偶数,那么就寻找把偶数变成奇数的最小操作次数,然后加上所有的偶数的个数。

代码

#include <bits/stdc++.h>
using namespace std;
int H[50];
void process()
{
    for(int i = 0; i <= 35; i++)
    {
        H[((long long)1 << i)%37] = i;
    }
}
int  main()
{
    process();
    int T;
    cin >> T;
    while(T--)
    {
        int ans;
        int num_even = 0;
        int minn = INT_MAX;
        int buf;
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &buf);
            if(H[(buf & (-buf))%37] != 0)
            {
                num_even ++;
            }
            minn = min(minn, H[(buf & (-buf))%37]);
        }
        if(minn == 0)
            ans = num_even;
        else
            ans = num_even-1+minn;
        printf("%d\n", ans);

    }
    return 0;
}

C.Manipulating History

在这里插入图片描述

思路

一定要从题目的描述中跳出来,题目说的很复杂,但是要找一种更加合理的规律。
如果直接按照题目的进行,显然是不可行的。
在这里插入图片描述
所以把所有字母全部统计一遍,如果不是偶数,那么就是开始的哪一个字母

代码

#include <bits/stdc++.h>
using namespace std;
#define N 200010
char buf[N];
int cnt[300];
int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        char ans;
        memset(cnt, 0, sizeof(cnt));
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= 2*n+1; i++)
        {
            scanf("%s", buf+1);
            int len = strlen(buf+1);
            for(int j = 1; j <= len; j++)
            {
                int p = buf[j];
                cnt[p] = (cnt[p]+1) % 2;
            }
        }
        for(int i = 'a'; i <= 'z'; i++)
        {
            if(cnt[i]&1 == 1)
            {
                ans = i;
                break;
            }
        }
        printf("%c\n", ans);
    }
    return 0;
}

D.The Enchanted Forest

在这里插入图片描述

思路

这一道题目中有两个贪心的维度

  1. 我要尽量把刚开始的蘑菇取的尽可能多。
    这样的贪心策略就是不走回头路。
  2. 第二,我要把新产生的蘑菇尽可能地贪完。
    由于蘑菇是在不断地生长,如果正过来考虑,会迷失方向,倒过来考虑,在给定时间内,长出的蘑菇是一个定值。而我每一个位置上的蘑菇与我最后到达这一点是在什么时候有关。
    如果是最后一次到的那么这里剩下的就是1个蘑菇,如果是最后一次是倒数第二次到的,那么这里剩下的就是2个蘑菇。。。使用在给定时间内,长出的蘑菇总量减去最后剩下的蘑菇,就是我得到的到的生长出来的蘑菇。容易得到,最后的路径一定是单向的(如果时间可以走完全图,那么最后几次一定是从最左面到最右面。如果不能走完全图,一定是某个点一直往一个方向)

所以分为两种情况:

  • 如果不能走完全图,那么就用尺取法,找到区间蘑菇最多的数目。然后加上第二种情况的最优值。
  • 否则就所有蘑菇加上第二种的最优值。

代码

#include <bits/stdc++.h>
using namespace std;
#define N 200010
int s[N];
int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        long long ans = 0;
        int n, k;
        scanf("%d%d", &n, &k);
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", s+i);
        }
        if(k >= n)
        {
            for(int i = 1; i <= n; i++)
            {
                ans += s[i];
            }
            ans += (long long)(k-1) * n;
            ans -= (long long)n * (n-1) / 2;
        }
        else
        {
            long long tmp = 0;
            int l = 1;
            int r = k;
            for(int i = 1; i <= r; i++) tmp += s[i];
            ans = tmp;
            while(r+1 <= n)
            {
                tmp-=s[l++];
                tmp+=s[++r];
                ans = max(tmp, ans);
            }
            ans += (long long)k*(k-1)/2;
        }
        printf("%lld\n", ans);
    }
    return 0;
}

E.Railway System

在这里插入图片描述

题目大意

有n个节点,m条边,你可以询问2m次,询问的m串中为1,代表包含这条边,如果为0,就代表不包含这条边。在这种情况下,会得到最大生成树的权值。
然后最后得出最小生成树的权值。

思路

这道题目需要知道最小生成树的克鲁斯卡尔算法。
对于最大生成树,可以进行类推:从把所有的边进行从大到小的排序,然后从最大的边开始,如果加上这一条边不会构成环,那么就选择这一条边。否则不选择这条边。

证明:按照树的定义:数的边数m是图的定点数n的n-1。

假设遇到一条边,满足与已经选择的边不构成回路,但是没有选择,那么在按照这种方法得到最大生成树之后,加上这条边,一定有回路。而这条边与比他大的边不会构成回路,那么只能是与比他小的边构成回路。这样的话,删除比他小的边,又称为了一颗树,但是权值比之前的大。
得证!

所以对于当前的最大的边,一定被选择在最大生成树里面。
如果把这条边删除,有两种情况

  1. 这条边是割边(删除之后图的连通分支会增加一)
    在这里插入图片描述
    那么删除之后,最大生成树的权值相比上一回,会减少这条边的权值。
  2. 如果删除之后还有回路,那么对于一种情况:使用回路来替代他,那么这种情况下,删除这条边的前后两次权值之差已经小于这条边的权值。如果还有更长的情况,那么删除前后权值的差值只会更小。

对于割边,最小生成树里面一定有它。而对于可以替代的边,最小生成树里面一定没有它。

对于下一次询问,删除这条边即可

  • 如果不是割边,那么一定不选它,直接删除,减小求解的范围。
  • 如果是割边,删除后也不会影响最大生成树的权值(删了可以缩小求解范围,使得下一条比他小的边一定在最大生成树里面)

对于2m次询问内容:

使用m次询问所有的边的长度

使用1次询问整个图的最大生成树

使用m-1次缩小范围。

最小的一条边一定在最小生成树里面。

代码

#include <bits/stdc++.h>
using namespace std;
priority_queue<pair<int, int> > pq;
int n, m;
int edge[600];

void ask(int k, bool tag)
{
    printf("? ");
    for(int i = 1; i <= k-1; i++)
    {
        if(tag) putchar('0');
        else putchar('1');
    }
    if(tag) putchar('1');
    else putchar('0');
    for(int i = k+1; i <= m; i++)
    {
        if(tag) putchar('0');
        else putchar('1');
    }
    putchar('\n');
    fflush(stdout);
}

void que()
{
    printf("? ");
    for(int i = 1; i <= m; i++)
    {
        printf("%d", edge[i]);
    }
    putchar('\n');
    fflush(stdout);
}
int main()
{
    int ans = 0;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= m; i++)
    {
        int buf = 0;
        ask(i, true);
        scanf("%d", &buf);
        pq.push(make_pair(buf, i));
    }
    for(int i = 1; i <= m; i++) edge[i] = 1;
    que();
    int res;
    int last;
    scanf("%d", &res);
    last = res;
    for(int i = 1; i <= m-1; i++)
    {
        pair<int, int>t;
        t = pq.top();
        pq.pop();
        edge[t.second] = 0;
        que();
        scanf("%d", &res);
        if(last - res < t.first);//并不需要干什么事情
        else
        {
            ans += t.first;
        }
        last = res;
    }
    ans += pq.top().first;
    printf("! ");
    printf("%d", ans);
    fflush(stdout);
    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值