Common Number(奇偶二分+找规律)

题意:给定函数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)/~

发布了414 篇原创文章 · 获赞 23 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/starlet_kiss/article/details/104209359