定义 \(f(x)=[x\bmod 2=1](x-1) + [x\bmod 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());
}