定义 (f(x)=[xmod 2=1](x-1) + [xmod 2=0] (x/2))
对于任意 (x),不断令 (x=f(x)) 进行迭代,最终会得到 (x=1),定义 (path_x) 为 (x) 在这个迭代过程中出现过的所有值的集合
给定 (n,k),求最大的 (y),使得有至少 (k) 个 (i in [1,n]) 使得 (y in path_i)
Solution
难度:L5
以下对于数的讨论都是在二进制意义下进行的
设 (f(i)) 表示 (i) 出现在 ([1,n]) 多少个数的 (path) 中,设 (g(i)) 表示 (i) 是 ([1,n]) 中多少个数二进制意义下的前缀
- 对于奇数 (i),(f(i)=g(i))
- 对于偶数 (i),(f(i)=g(i)+g(i+1))
由于 (f) 序列的奇数项和偶数项分别单调,于是我们分别二分,找到最后一个 (f(i) geq k) 的奇数项和偶数项即可
考虑如何计算 (g(i)),首先我们找到一个最小的长度 (len),使得 (i2^{len+1} >n),然后二分找到 (mid) 使得 (i 2^{len}+mid leq n),显然可以二分得到,那么 (g(i)=mid+2^{len})
总体复杂度 (O(log^2 n))
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n,k;
int g(int i) {
if(i==0) return 0; //!
int l=0,r=n,len=0;
while(i*(1ll<<(len+1))<=n) ++len;
r=1ll<<len;
while(l<r) {
int mid=(l+r)/2;
int t=mid;
if(i*(1ll<<len)+mid<=n) l=mid+1;
else r=mid;
}
int mid=l-1;
int t=mid;
//cout<<"calc "<<i<<" "<<mid<<" "<<len<<endl;
return mid+(1ll<<len);
}
int f(int i) {
if(i&1) return g(i);
return g(i)+g(i+1);
}
int sol1() {
int l=0,r=n;
while(l<r) {
int mid=(l+r)/2;
int xmid = mid*2;
if(f(xmid)>=k) l=mid+1;
else r=mid;
}
return (l-1)*2;
}
int sol2() {
int l=0,r=n;
while(l<r) {
int mid=(l+r)/2;
int xmid = mid*2+1;
if(f(xmid)>=k) l=mid+1;
else r=mid;
}
return (l-1)*2+1;
}
signed main() {
//ios::sync_with_stdio(false);
cin>>n>>k;
cout<<max(sol1(),sol2());
}