【codeforces 431D】Random Task
【题目链接】:http://codeforces.com/problemset/problem/431/D
【题意】
给你一个数字m和数字k;
让你找一个数字x;
使得x+1..2*x这个区间范围内的数,它们的二进制形式里面,1的个数恰好为k个的数字恰好有m个;
【题解】
首先需要知道x+1..2*x这里面二进制1的个数的为k的数字的个数是随着x单调不下降的;
比如
n = x=>x+1,x+2...2*x
n = x+1=> x+2...2*x,2*x+1,2*x+2
这里x+1的1的个数和2*x+2的1的个数是一样的;
因为乘2的话,会让所有的1整体左移;但1的个数不变;
这样
x+1,x+2…2*x
x+2…2*x,2*x+2
它们俩含k个1的个数是相同的;
但是n=x+1的时候还有一个2*x+1;这个数字可能是含k个1的,所以说是单调不下降的;
知道单调性之后;
就二分n吧.
然后求某个范围(L,R]里面二进制有k个的1的数的个数;
可以先求出1..L和1..R这两个区间范围内的有k个1的数的个数;
然后相减就是这个区间的了;
而求1..x中有k个1的数的个数;
是个计数的问题了;
具体的,把x的二进制形式中的某个1给变成0;
(计数完之后还原就是了);
然后假设这是从右往左数第i位;
则在i-1个数字中选择剩下的所需的1的个数就好(k减去这一位左边的1的个数);
(因为第i位是1,所以变成0后,后面可以出现任意数量的1);
如果x也是k个1别忘了算上;
【Number Of WA】
0
【完整代码】
#include <bits/stdc++.h>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define LL long long
#define rep1(i,a,b) for (int i = a;i <= b;i++)
#define rep2(i,a,b) for (int i = a;i >= b;i--)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define ms(x,y) memset(x,y,sizeof x)
#define Open() freopen("F:\\rush.txt","r",stdin)
#define Close() ios::sync_with_stdio(0),cin.tie(0)
typedef pair<int,int> pii;
typedef pair<LL,LL> pll;
const int dx[9] = {0,1,-1,0,0,-1,-1,1,1};
const int dy[9] = {0,0,0,-1,1,-1,1,-1,1};
const double pi = acos(-1.0);
const int N = 66;
LL m,k;
LL c[N][N],t[N];
void pre(){
t[0] = 1;
rep1(i,1,63){
t[i] = t[i-1]*2;
}
rep1(i,0,65)
c[i][0] = c[i][i] = 1;
rep1(i,1,65){
rep1(j,1,i-1){
c[i][j] = c[i-1][j]+c[i-1][j-1];
}
}
}
LL get_sum(LL x){
int cur = 0;
LL temp = 0;
for (LL i = 63;i >= 0;i--)
if (x&t[i]){
if (k-cur>=0)
temp += c[i][k-cur];
cur++;
}
if (cur==k) temp++;
return temp;
}
LL check(LL n){
LL temp1 = get_sum(2*n);
LL temp2 = get_sum(n);
temp1-=temp2;
return temp1;
}
int main(){
//Open();
Close();//scanf,puts,printf not use
//init??????
pre();
cin >> m >> k;
LL l = 1,r = (LL) 1e18,ans = 0;
while (l <= r){
LL mid = (l+r)>>1;
LL temp = check(mid);
if (temp>=m){
ans = mid;
r = mid - 1;
}else {
l = mid + 1;
}
}
cout << ans << endl;
return 0;
}