前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Codeforces Round 671 (Div. 2) A-D

Codeforces Round 671 (Div. 2) A-D

作者头像
ACM算法日常
发布2020-09-27 10:44:10
3690
发布2020-09-27 10:44:10
举报
文章被收录于专栏:ACM算法日常ACM算法日常

A. Digit Game

题意:

给出一个长度为 n 的数列(下标从1开始),A和B两个人在玩游戏,规定A只能从奇数的位置取数,B只能从偶数的位置取数,当且仅当数列中剩下一个数时游戏结束,如果剩下的这个数是奇数,则A获胜,否则B获胜,若从A开始取数,且双方都用最优的策略进行游戏,问最后谁能取胜。

思路:

如果 n 已经确定时,最后剩下的那个位置的奇偶性也随之确定了,如果 n 是奇数的话,因为奇数多一个,所以最后剩下的那个数显然是奇数,同理如果 n 是偶数的话,因为奇数先取,所以最后会剩下一个偶数。所以如果 n 是奇数的话,只要奇数位置存在一个奇数,A 保留这个奇数直到最后就能赢,同理当 n 是偶数时,B 保留一个偶数直到最后就能赢,根据 n 的奇偶分类讨论一下即可。

代码语言:javascript
复制
#include<iostream>
#include<cstdio>
using namespace std;
const int MAX_N=1e5+100;
char s[MAX_N];
int main(void){
    int T;
    cin>>T;
    while(T--){
        int n;
        scanf("%d%s",&n,s);
        if(n&1){//剩下一个奇数
            bool flag=false;
            for(int i=0;i<n;i+=2){
                if((s[i]-'0')%2!=0)
                flag=true;
            }
            if(flag)
            printf("1\n");
            else
            printf("2\n");
  }
        else{//剩下一个偶数
            bool flag=false;
            for(int i=1;i<n;i+=2)
            if((s[i]-'0')%2==0)
            flag=true;
            if(flag)
            printf("2\n");
            else
            printf("1\n");
        }
    }
    return 0;
}

B. Stairs

题意:

首先规定 n 阶楼梯的定义是,对于每一层的高度而言,第一个位置高度为 1,第二个位置高度为 2 ,... 第 n 个位置高度为 n,再规定 "nice" 楼梯的定义是,n 阶楼梯可以被划分为恰好 n 个互不相交的正方形,现在问用 x 个方块,最多可以同时构造多少种互不相同的 “nice” 楼梯。

思路:

首先不难看出,n 最小的 “nice” 楼梯是当 n = 1 的时候,其次就是 n = 3 的时候了,再结合样例中给出的 n = 7 时的 “nice” 楼梯,可以观察出,第 x + 1 个 “nice” 楼梯是由两个第 x 个“nice”楼梯加上一个正方形拼接而成的,而每次的 n 对应的变成 n * 2 + 1。因为题目中需要尽可能多的同时构造“nice”楼梯,所以优先构造出 n 比较小的楼梯显然是最优的,又因为“nice”楼梯的 n 每次都会增长到两倍还要多,也就保证了时间复杂度是 log 级别的,对于 n 阶楼梯而言,其需要用到的方块呈等差数列,即

1+2+3+...+n=\frac{n*(n+1)}{2}

,剩下的直接模拟即可。

代码语言:javascript
复制
#include<iostream>
#include<cstdio>
using namespace std;
long long cal(long long n){
    return n*(n+1)/2;
}
int main(void){
    int T;
    cin>>T;
    while(T--){
        long long n;
        scanf("%lld",&n);
        int ans=0,pos=1;
        while(cal(pos)<=n){
            n-=cal(pos);
            pos=pos*2+1;
            ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}

C. Killjoy

题意:

给出 n 个人,每个人都有一个权值 a[ i ] ,现在模拟传播病毒的过程,初始时有一个权值为 x 的人感染了病毒(不属于这 n 个人之中),规定如果有一个人感染了病毒,那么所有与其权值相同的人都将会被传染,现在可以进行操作,每次操作可以将 n 个人的权值任意赋值,不过需要保证每个人权值变化的delta总和为0,问最少需要进行多少次操作,可以使所有人都感染病毒。

思路:

不难看出,对于一般情况而言,最多进行两次操作就能使所有人都感染病毒,具体操作如下:将 n 个人分为两组,第一组只有随便一个人,第二组有剩余的 n - 1 个人,第一次操作将第一组的这个人赋值为 x,用第二组的人进行平衡(因为需要满足

\sum delta = 0

),这样第一组的这一个人就被感染了,第二次操作可以将第二组的 n - 1 个人都赋值为 x ,然后让第一组的这一个人进行平衡,这样 n 个人就都能被感染了。考虑什么时候答案为 0 或 1。题目中样例已经给提示了,当 n 个人都为 x 时,显然答案为 0 ,当 n 个人的平均值为 x 时,仅用一次操作就可以将 n 个人都变为 x ,从而达成目标,最后还剩一种情况不算容易想到,就是如果前面的条件都不满足,但是整个数列中存在这至少一个数等于 x 的话,那么在进行一般操作时,将这些等于 x 的数分成第一组,就可以省略掉一次操作,从而使得总操作数为 1 了,实现的话就比较简单了,分类讨论一下就好。

代码语言:javascript
复制
#include<iostream>
#include<cstdio>
using namespace std;
int main(void){
    int T,n,x,i;
    cin>>T;
    while(T--){
        scanf("%d%d",&n,&x);
        int delta=0,cnt=0,num;
        for(i=1;i<=n;i++){
            scanf("%d",&num);
            if(num==x)
            cnt++;
            delta+=num-x;
        }
        if(cnt==n)
        printf("0\n");
        else if(cnt||delta==0)
        printf("1\n");
        else
        printf("2\n");
    }
    return 0;
}

D. Sage's Birthday

题意:

给出 n 个物品,每个物品都有权值,现在需要对其重新排列,使得 a[ i ] < a[ i - 1 ] && a[ i ] < a[ i +1 ] 的位置尽可能多。

思路:

根据题目中的样例可以知道,第一个位置和最后一个位置是不能提供贡献的,且提供贡献的位置显然互相间隔为 1 是最优的,这样可以使得相对大小关系之间的利用最大化,回顾一下可以提供贡献的位置是 a[ i ] < a[ i + 1 ] && a[ i ] < a[ i - 1 ] ,所以我们可以贪心去放置 a[ i - 1 ] 和 a[ i + 1 ] 的这些位置,即

2 * k - 1

的位置(下标从1开始,满足

2*k-1>=1 且 2*k-1<=n

),显然是把较大的

\lceil \frac{n}{2} \rceil

个数字依次放在这些位置是最优的,此时还剩下了

\lfloor\frac{n}{2}\rfloor

个数字尚未填充,因为我们刚刚是按照降序的顺序填充的

2*k-1

的位置,所以取三个已经填充的位置 a[ i ] , a[ i + 2 ] 和 a[ i + 4 ] ,满足 a[ i ] >= a[ i + 2 ] >= a[ i + 4 ] ,此时 a[ i + 1 ] 和 a[ i + 3 ] 需要填充数值来尽可能多的提供贡献,假设在 a[ i + 1 ] 处填充了一个 x ,在 a[ i + 3] 处填充了一个 y ,且都对答案提供了一个单位的贡献,因为 x < a[ i + 2 ],y < a[ i + 4 ] ,且 a[ i + 2 ] >= a[ i + 4 ] ,则肯定有 x >= y ,再讨论两种情况,如果某个数 x 在 a[ i + 1 ] 处可以提供一个单位的贡献,那么将 x 放置在 a[ i + 3 ] 的位置必然也是可以提供贡献的,但是反之,如果某个数 x 在 a[ i + 3 ] 可以提供一个单位的贡献,将其放置在 a[ i + 1 ] 的位置不一定可以提供贡献,所以贪心策略呼之欲出,对于 a[ i - 1 ] 和 a[ i + 1 ] 而言,a[ i ] 中放置小于 min( a[ i - 1 ] , a[ i + 1 ] ) 的最大的那个数是最优的,对于那些无处可放的数,最后随便放就行了,因为无论放到哪里都不会产生贡献,只要不影响前面的数就好。

代码语言:javascript
复制
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAX_N=1e5+100;
int a[MAX_N],b[MAX_N];
bool vis[MAX_N];
bool cmp(int a,int b){
    return a>b;
}
int main(void){
    int n,i,j;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    scanf("%d",&a[i]);
    sort(a+1,a+1+n,cmp);
    for(i=1,j=1;i<=n;i+=2){
        vis[j]=true;
        b[i]=a[j++];
    }
    int ans=0;
    for(i=2;i<=n;i+=2){
        while(j<=n&&a[j]>=b[i+1])
        j++;
        if(j<=n){
            vis[j]=true;
            b[i]=a[j++];
            ans++;
        }
    }
    for(i=1,j=1;i<=n;i++){
        if(!b[i]){
            while(vis[j])
            j++;
            b[i]=a[j++];
        }
    }
    printf("%d\n",ans);
    for(i=1;i<=n;i++)
    printf("%d ",b[i]);
    return 0;
}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-09-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ACM算法日常 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档