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进行排序·····