自从省赛结束后,我就很少刷题了,虽然泡ACM不久,仅有一年多点,但是这已经是我的生活习惯了,有些东西不是说放弃就能放弃的,我想有可能我也想参加区预赛。
就算不能参加,我也不能放弃ACM,我决定每天早上早起来刷至少一题再弄其他,记得早起的时候要追溯到学习java的时光了……好啦,不废话了~
A题,签到题,看着样例都能过了。
/*************************************************************************
> OS : Linux 3.2.0-60-generic #91-Ubuntu
> Author : yaolong
> Mail : dengyaolong@yeah.net
> Created Time: 2014年05月21日 星期三 23:25:30
************************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
char str[100005];
int main(){
int a[5],sum=0;
int len=strlen(str);
scanf("%d%d%d%d",a+1,a+2,a+3,a+4);
scanf("%s",str);
while(len--){
sum+=a[str[len]-'0'];
}
printf("%d\n",sum);
return 0;
}
B题,O(n!)的算法,算是比较恶心的暴力题,不过还是很简单。遍历所有的可能,再根据给出的公式,毫无压力。
/*************************************************************************
> OS : Linux 3.2.0-60-generic #91-Ubuntu
> Author : yaolong
> Mail : dengyaolong@yeah.net
> Created Time: 2014年05月21日 星期三 23:25:46
************************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int a[10][10];
int count(int i1,int i2,int i3,int i4,int i5){
int r1= a[i1][i2]+a[i2][i1]+a[i3][i4]+a[i4][i3];
int r2= a[i2][i3]+a[i3][i2]+a[i4][i5]+a[i5][i4];
int r3= a[i3][i4]+a[i4][i3];
int r4= a[i4][i5]+a[i5][i4];
return r1+r2+r3+r4;
}
int main(){
int i,j;
for(i=1;i<=5;i++){
for(j=1;j<=5;j++){
scanf("%d",&a[i][j]);
}
}
int mmax=-1;
int i1,i2,i3,i4,i5;
for(i1=1;i1<=5;i1++){
for(i2=1;i2<=5;i2++){
if(i1==i2){
continue;
}
for(i3=1;i3<=5;i3++){
if(i1==i3||i2==i3){
continue;
}
for(i4=1;i4<=5;i4++){
if(i1==i4||i2==i4||i3==i4){
continue;
}
for(i5=1;i5<=5;i5++){
if(i1==i5||i2==i5||i3==i5||i4==i5)
{
continue;
}
int tmp=count(i1,i2,i3,i4,i5);
if(tmp>mmax) mmax=tmp;
}
}
}
}
}
printf("%d",mmax);
return 0;
}
D题,当时我没有做下去,因为我困了~我玩cf不刷rating求的是气氛,所以累了我就休息了。
这题目相对没那么简单,也蛮有意思的题目。
题目就是:给定m,k,输出一个n(多种解),使得n+1,n+2,……,2*n中恰好有m个数的二进制恰好有k个1。比如m=3,k=2时候,n=6为其中一个解。
从(110,1100],有且只有1001,1010,1100(9,10,12)这三个数的二进制的1的个数为2。
思路:首先,我们要知道这是一个随n递增的函数,非常容易证明:n+1,n+2,..,(2*n)中二进制恰好有k个个数为p,当n++后,即n+2,....2*n,2*n+1,2*(n+1),注意到2*(n+1)与n+1的二进制1的个数是一样的,那么引起改变的只有2*n+1,即最多不减。
那么我们就可以用二分来搞了,二分的思路就是,计算rt=10^8,lt=1,那么我们计算(1~2*mid)-(1~mid)满足的个数如果等于m就break掉,比m小lt=mid+1,否则rt=mid-1。
接下来的问题就是如何搞1~X中恰好为k的个数了,其实也是很简单的。我们用一个计数器,表示已经统计了多少位,从最高位往最低搞起,当前“1”位是第i个(起始从0开始啊!),那么我们可以有C(i,k-cnt)个搞法(从第0位到第i-1各中选出k-cnt个,大于等于i的位全部已经定下来了)之后cnt++。(记得要判断k>cnt,不然就像我一开始WA无限)。
另外,这道题的组合数果断用杨辉三角来搞啊!
/*************************************************************************
> OS : Linux 3.2.0-60-generic #91-Ubuntu
> Author : yaolong
> Mail : dengyaolong@yeah.net
> Created Time: 2014年05月22日 星期四 07:19:55
************************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long LL;
LL C[70][70],m;
int k;
void init(){
int i,j;
for( i=0;i<=64;i++){
C[i][0]=C[i][i]=1;
}
for(i=2;i<=64;i++){
for(j=1;2*j<=i;j++){
C[i][j]=C[i][i-j]=C[i-1][j-1]+C[i-1][j];
}
}
}
LL F(LL X){
LL ans=0;
int i,cnt=0;
int max_bit=0;
LL tmp=X;
while(tmp>1){
max_bit++;
tmp>>=1;
}
for ( i=max_bit;i>=0;i--){
if((X>>i)&1){
if(k>cnt)
ans+=C[i][k-cnt];
cnt++;
}
}
if(cnt==k)ans++;
return ans;
}
LL cal(LL num){
return F(num*2)-F(num);
}
int main(){
init();
cin>>m>>k;
LL lt=1,rt=1000000000000000000LL;
LL ans=0;
while(lt<=rt){
LL mid=(lt+rt)>>1;
LL tmp=cal(mid);
if(tmp==m){
ans=mid;
break;
}
if(tmp<m){
lt=mid+1;
}else{
rt=mid-1;
}
}
cout<<ans;
return 0;
}