题面

给定整数CSP模拟赛 number (二分+数位DP)_时间复杂度,求出最小和最大的正整数 CSP模拟赛 number (二分+数位DP)_c++_02 使得 CSP模拟赛 number (二分+数位DP)_数位_03 中恰好有 CSP模拟赛 number (二分+数位DP)_数位_04 个数 在二进制下恰好有 CSP模拟赛 number (二分+数位DP)_数位_05CSP模拟赛 number (二分+数位DP)_c++_06。如果有无数个满足条件则输出一行一个整数CSP模拟赛 number (二分+数位DP)_数位_07。有CSP模拟赛 number (二分+数位DP)_时间复杂度_08组数据。
CSP模拟赛 number (二分+数位DP)_时间复杂度_09
CSP模拟赛 number (二分+数位DP)_c++_10
CSP模拟赛 number (二分+数位DP)_c++_11
保证CSP模拟赛 number (二分+数位DP)_数位_12内 存在一个数满足条件。

题解

CSP模拟赛 number (二分+数位DP)_数位_13表示CSP模拟赛 number (二分+数位DP)_数位_14中在二进制下为恰好有CSP模拟赛 number (二分+数位DP)_数位_05CSP模拟赛 number (二分+数位DP)_c++_06的数的个数。可以证明打表发现CSP模拟赛 number (二分+数位DP)_数位_13是单调不降的。所以就可以二分了。二分后数位CSP模拟赛 number (二分+数位DP)_c++_18就行了。计算CSP模拟赛 number (二分+数位DP)_数位_13可以用CSP模拟赛 number (二分+数位DP)_数位_20,其中CSP模拟赛 number (二分+数位DP)_数位_21表示CSP模拟赛 number (二分+数位DP)_c++_06CSP模拟赛 number (二分+数位DP)_数位_23的数中在二进制下恰好有CSP模拟赛 number (二分+数位DP)_数位_05CSP模拟赛 number (二分+数位DP)_c++_06的数的个数。

因为数位CSP模拟赛 number (二分+数位DP)_c++_18的数组是可以多次用的,所以时间复杂度是整体两个CSP模拟赛 number (二分+数位DP)_数位_27的,而每次时间复杂度为CSP模拟赛 number (二分+数位DP)_时间复杂度_28

但是这里的二分上界并不是CSP模拟赛 number (二分+数位DP)_数位_12,因为保证最小的CSP模拟赛 number (二分+数位DP)_c++_02CSP模拟赛 number (二分+数位DP)_数位_12内,但是最大的可能超出。所以上界我设的CSP模拟赛 number (二分+数位DP)_时间复杂度_32

输出CSP模拟赛 number (二分+数位DP)_数位_07的情况只有一种就是CSP模拟赛 number (二分+数位DP)_时间复杂度_34,在CSP模拟赛 number (二分+数位DP)_时间复杂度_35时满足。

CODE

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
ULL m, f[70][70];
int k, c[70], n;
bool vis[70][70];
ULL dfs(int i, int j, bool fp) {
if(!fp && vis[i][j]) return f[i][j];
if(i == 0) return j == 0;
ULL ans = 0; int mx = fp ? c[i] : 1;
for(int d = 0; d <= mx; ++d) ans += dfs(i-1, j-d, fp&&d==mx);
if(!fp) vis[i][j] = 1, f[i][j] = ans;
return ans;
}
ULL cal(ULL x) {
for(n=0; x; c[++n]=x&1,x>>=1);
return dfs(n, k, 1);
}
ULL chk(ULL x) { return cal(2*x) - cal(x); }
int main() {
int T; scanf("%d", &T); while(T--) {
scanf("%llu%d", &m, &k);
if(k == 1) puts("-1");
else {
ULL l = 1, r = 1llu<<63, mid;
while(l < r) {
mid = (l + r) >> 1;
if(chk(mid) >= m) r = mid;
else l = mid+1;
}
ULL L = 1, R = 1llu<<63, MID;
while(L < R) {
MID = (L + R) >> 1;
if(chk(MID) > m) R = MID;
else L = MID+1;
}
printf("%llu %llu\n", l, L-1);
}
}
}