CSP模拟赛 number (二分+数位DP)
原创
©著作权归作者所有:来自51CTO博客作者AAArk的原创作品,请联系作者获取转载授权,否则将追究法律责任
题面
给定整数,求出最小和最大的正整数 使得 中恰好有 个数 在二进制下恰好有 个 。如果有无数个满足条件则输出一行一个整数。有组数据。
保证内 存在一个数满足条件。
题解
设表示中在二进制下为恰好有个的数的个数。可以证明打表发现是单调不降的。所以就可以二分了。二分后数位就行了。计算可以用,其中表示到的数中在二进制下恰好有个的数的个数。
因为数位的数组是可以多次用的,所以时间复杂度是整体两个的,而每次时间复杂度为
但是这里的二分上界并不是,因为保证最小的在内,但是最大的可能超出。所以上界我设的
输出的情况只有一种就是,在时满足。
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);
}
}
}