题意:给定函数f(x),x为偶数时f(x)=x/2,x为奇数时f(x)=x-1
给定n,k,对1到n每个数求f(x)的轨迹,如path[15]={15,14,7,6,3,2,1},求在所有轨迹里出现次数大于等于k的最大值ans。
思路:我们将n个数的结构画出来,就会发现这n个数组成一个树结构。把所有的数字出现的次数找出来,就会发现一个规律,由小到大,分奇偶呈现递减趋势。
例如n=14,第1 3 5 7 9 11 13的次数分别为14 6 3 2 1 1 1。第2 4 6 8 10 12 14的次数分别为13 6 3 2 2 2。那么我们就可以分别对奇偶二分,寻找最优答案。接下来最主要的就是怎么计算每个数字的次数了。
如图所示(图片摘自参考博客):
对于2这个点来说,出现次数=(3-2+1)+(7-4+1)+(15-8+1)+(31-16+1)。
对于5这个点来说,出现次数=(5-5+1)+(11-10+1)+(23-20+1)。
每一层的最右边点和最左边点分别呈现 上一层最右边数字2以及上一层最左边数字2+1的规律。然后配合二分,就可以了。
代码如下:
#include<bits/stdc++.h>
#define ll long long
#define inf 1e18
using namespace std;
ll n,k;
inline ll check(ll x)
{
queue<pair<ll,ll> >p;
if(x&1) p.push(make_pair(x,x));
else p.push(make_pair(x,x+1));
ll ans=0;
while(p.size())
{
pair<ll,ll> zz=p.front();
p.pop();
ans+=min(zz.second,n)-zz.first+1;
if((zz.first<<1)<=n) p.push(make_pair(zz.first<<1,zz.second<<1|1));
}
return ans;
}
int main()
{
scanf("%lld%lld",&n,&k);
ll l=1ll,r=(n%2)?n:n-1;
ll ans=0;
while(l<=r)
{
ll mid=l+r>>1ll;
if((mid%2ll==0)) mid--;
ll cnt=check(mid);
if(cnt>=k)
{
ans=max(ans,mid);
l=mid+2ll;
}
else r=mid-2ll;
}
l=2ll,r=(n%2)?n-1:n;
while(l<=r)
{
ll mid=l+r>>1ll;
if(mid%2ll==1) mid--;
ll cnt=check(mid);
if(cnt>=k)
{
ans=max(ans,mid);
l=mid+2ll;
}
else r=mid-2ll;
}
cout<<ans<<endl;
}
努力加油a啊,(o)/~