牛客月赛 40补题

比赛链接

A 数字游戏

题意

二进制中有奇数个1,最后一位 0变1 或 1变0
偶数个1,最高位1变0
问:几次能全部变成0?

思路

首先分成两大部分,奇数个1 和 偶数个1
然后在此基础上再分尾数是1和0的情况,
所以就有4种情况讨论,通过模拟小的样例得规律!!!

知识点

二进制数末尾为1,便为奇数,末尾为0,则为偶数
n&1判断奇偶,偶数为0,奇数为1
n>>i可以取n的二进制的末尾第i个数,如n=4,100,n>>3=1

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        int a[35];
        int ans=0;
        int cnt=0;
        scanf("%d",&n);
        
        for(int i=0;i<32;i++)//获取n的二进制形式的数
        {
            int k=n>>i;
            if(k&1)//此时k为1
            {
                a[++cnt]=1;
            }
            else
            {
                a[++cnt]=0;
            }
        }
        
        if(a[1]==1)//末尾为1的,为奇数
        {
            int sum=0;
            for(int i=2;i<=cnt;i++)
            {
                if(a[i]==1)
                    sum++;
            }
            if(sum&1)
                ans=sum*2;
            else
                ans=2*sum+1;
        }
        else//偶数
        {
            int sum=0;
            for(int i=2;i<=cnt;i++)
            {
                if(a[i]==1)sum++;
            }
            if(sum&1)
                ans=2*sum+1;
            else
                ans=sum*2;
        }
        printf("%d\n",ans);
    }
    return 0;
}

B

E 分组

dd当上了宣传委员,开始组织迎新晚会,已知班里有nn个同学,每个同学有且仅有一个擅长的声部,把同学们分成恰好mm组,为了不搞砸节目,每一组里的同学都必须擅长同一个声部,当然,不同组同学擅长同一个声部的情况是可以出现的,毕竟一个声部也可以分成好几个part进行表演,但是他不希望出现任何一组的人过多,否则可能会导致场地分配不协调,也就是说,她希望人数最多的小组的人尽可能少,除此之外,对组内人员分配没有其他要求,她希望你告诉她,这个值是多少,如果无法顺利安排,请输出-1

输入描述:

第一行两个数个数n,m(1≤m≤n≤100000)表示人数
接下来一行n个数,ai表示第i个学生的擅长声部

输出描述:

输出一个数,表示人数最多的小组的人数
示例1

输入

5 3
2 2 3 3 3

输出

2

二分+最小化最大值
while(l<r){
int mid=(l+r)>>1;
if(check(mid))r=mid;//如果这里要改成r=mid-1,那么上面要改成l<=r
else l=mid+1;//用二分搜出最小的最大值(人数最多的人数)
}

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,num[N];
vector<int>a;
bool check(int x)
{
    int tot=0;
    for(int u:a)tot+=(num[u]+x-1)/x;//谁能给我解释解释啊哭了,我只知道这样可行
    return tot<=m;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        int x;cin>>x;
        if(!num[x])a.push_back(x);//新思路啊哈
        num[x]++;
    }
    if(a.size()>m){
        cout<<-1<<endl;
        return 0;
    }
    int l=1,r=n;
    while(l<r){
        int mid=(l+r)>>1;
        if(check(mid))r=mid;//如果这里要改成r=mid-1,那么上面要改成l<=r
        else l=mid+1;//用二分搜出最小的最大值(人数最多的人数)
    }
    cout<<l<<endl;
    return 0;
}

另一种方法

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
const int N=1e5+10;
int a[N];
bool check(int mid)
{
    int sum=0;
    for(int i=1;i<=n;i++)
        sum+=(a[i]+mid-1)/mid;
    return sum<=m;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        int x;
        cin>>x;
        a[x]++;
    }
    int l=1,r=1e5,ans=-1;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(check(mid))ans=mid,r=mid-1;//注意它这里取的是mid,而上一种二分写法取的是l
        else l=mid+1;
    }
    cout<<ans<<endl;
    return 0;
}

G 空调遥控

dd作为集训队的队长,一直掌管着集训室的空调遥控器,她需要调整温度使队员们更好地进入训练状态,已知集训室一共有nn名队员,每位队员都有一个温度诉求a[i] ,1≤i≤n,当室内温度为K时,当且仅当∣a[i]−K∣≤p时,这个队员能够正常进入训练状态,否则就会开始躁动,作为队长,dddd需要调整好温度,她想知道,在最佳情况下,最多有多少队员同时进入训练状态

输入描述:

第一行两个数n,p(1≤n,p≤1000000),含义如题面描述
接下来一行n个数a[i], 1≤a[i]≤1000000表示每个队员的温度诉求

输出描述:

输出一个数字,表示最多有多少队员同时进入训练状态

输入

6 2
1 5 3 2 4 6

输出

5
温度调成3或4,都可以满足5名队员同时进入训练状态

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;
int a[N];
ll sum[N];

int main()
{
    int n,p;
    cin>>n>>p;
    for(int i=1;i<=n;i++)
    {
        int x;
        cin>>x;
        a[x]++;
    }
    ll ans=0;
    for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i];//前缀和,我承认我看错了题目,该死滴555
    for(int i=p+1;i<=N-p;i++)
    {
        ll maxn=sum[i+p]-sum[i-p-1];//一个区间里共有的人数
        ans=max(ans,maxn);
    }
    cout<<ans<<endl;
    return 0;
}
知识点
二分查找,在一个排好序的数组中进行查找

lower_bound(num,num+n,k)-num
upper_bound(num,num+n,k)-num

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int num[6]={1,2,4,7,15,34};
    sort(num,num+6);
    int pos1=lower_bound(num,num+6,2)-num;//返回数组中第一个大于或等于被查数的下标,比如这里就是1,2
    int pos2=upper_bound(num,num+6,2)-num;//返回数组中第一个大于被查数的下标,比如这里就是2,4
    cout<<pos1<<" "<<num[pos1]<<endl;
    cout<<pos2<<" "<<num[pos2]<<endl;
    return 0;
}



另一种方法,二分查找,我第一种想法就是这个,但我不会灵活用,大笨蛋枯枯5555

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;
int a[N];


int main()
{
    int n,p,ans=1;
    cin>>n>>p;
    for(int i=1;i<=n;i++)cin>>a[i];
    sort(a+1,a+n+1);//lower_bound()二分查找,在一个排好序的数组中进行查找
    for(int i=2;i<=n;i++)
    {
        int pos=lower_bound(a+1,a+i,a[i]-2*p)-a;//以a[i]-p为温度,长度为[a[i]-2*p,a[i]]
        ans=max(ans,i-pos+1);//pos返回的是第一个大于等于a[i]-2*p的下标
    }//ans返回的是跨度最长的区间长度,跨度越大包含的符合需求的人数越多
    cout<<ans<<endl;
    return 0;
}

I 体操队形

题目描述

dddd作为体操队队长,在给队员们排队形,体操队形为一个单独的纵列,体操队有nn个同学,标号为1\sim n1∼n,对于i(1≤i≤n)i(1≤i≤n)号队员,会有一个诉求(1≤a[i]≤n)(1≤a[i]≤n),表示他想排在a[i]a[i]号队员前面,当a[i]=ia[i]=i时,我们认为他没有位置需求,随便排哪儿都行,dddd想知道有多少种队形方案,可以满足所有队员的要求。

输入描述:

读入第一行一个数字n(2≤n≤10)
第二行n个数字,表示a[i],保证1≤a[i]≤n

输出描述:

输出一行,表示方案数
示例1

输入

3
1 1 2

输出

1

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[20],p[20],pos[20];
int main()
{
    int n;
    int ans=0;
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=n;i++)p[i]=i;
    do{
        for(int i=1;i<=n;i++)pos[p[i]]=i;//每次都恢复正常下标
        bool f=1;
        for(int i=1;i<=n;i++)
        {
            if(pos[i]>pos[a[i]])f=0;//用逆向思维,只要一项符合就不能满足所有队员的要求
        }
        if(f)ans++;
    }while(next_permutation(p+1,p+1+n));//当前序列不存在下一个排列时,函数返回false,否则返回true
    cout<<ans;
    return 0;
}


知识点

next_permutation(start,end)当前排列不存在下一个排列时,函数返回false,否则返回false,否则返回true.
next_permutation(node,node+n,cmp)对数组num中的前n个元素进行全排列,同时并改变num数组的值
prev_permutation(start,end)当前排列的上一个排列

#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
   int num[3]={1,2,3};
   do
   {
      cout<<num[0]<<" "<<num[1]<<" "<<num[2]<<endl;
   }while(next_permutation(num,num+3));
   return 0;
}
输出结果为
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

但当把while(next_permutation(num,num+3))中的3改成2时,输出就变成了

1 2 3
2 1 3

由此看出,next_permutation(num,num+n)函数是对数组num前n个元素进行全排列,同时并改变num数组的值(也就是下标不变,下标对应的数改变了)。

还有,next_permutation()在使用前需要对欲排列数组按升序排序,否则只能找出该序列之后的全排列数。比如如果数组num初始化为2,3,1,那么输出就变成了2 3 1
3 1 2
3 2 1
此外,next_permutation(node,node+n,cmp)可以对结构体num按照自定义的排列方式cmp进行排序·····

  • 18
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值