CFdiv2-Common Number-(奇偶数二分+规律)

E

题意:
就是给你一个函数f(x) = x/2(如果x是偶数),f(x) = x-1(如果x是奇数且不为1)。定义path(15) = [15,14,7,6,3,2,1]。也就是他会走的数字直到为1。然后给你一个k,问你哪个数字在>=k个不同的path中出现过,如果有多个,输出最大的那个数。

思考:

  1. 既然问最大的,那么肯定可以二分,手推了推发现越小的数字出现在的path越多,那么就肯定是满足单调性的。然后比如check(mid),怎么算mid出现的path?
  2. 如果x是奇数,那么x包含在path(x),path(2x),path(2x+1),path(4x),path(4x+1),path(4x+2),path(4x+3)。发现最初的起点就是x第一层一个数,然后一直往下面拓展,每拓展一次就是一层的数。
    如果x是偶数,那么x包含在path(x),path(x+1),path(2x),path(2x+1),path(2x+2),path(2x+3)。发现最初的起点是[x,x+1]第一层两个数,然后拓展,每次拓展就是一层。
  3. 刚开始我这样想的时候,感觉你这递归多少次呀,肯定超时了,虽然看起来像log,但是如果暴力dfs去搜每个分支这就是O(n)的。但是如果我每次只存这一层的两个端点呢?那么是不是就log(n)了。为什么每次都是一层呢?画图会发现,这就是一层一层连续的数。
  4. 图片引自其他博客
  5. 然后我就去写了,但是发现不对呀。看了看图发现,并不是整个数都是单调的,而是分奇数偶数的。1>3>5>7…,2>4>6>8…,但是2并不大于3。所以两次二分,一次二分奇数一次二分偶数,每次取最大值就可以了,注意奇数偶数二分的写法。

代码:

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define db double
#define int long long
#define PII pair<int,int >
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
		
using namespace std;
const int mod = 1e9+7,inf = 1e18;
const int N = 2e5+10,M = 2010;

int T,n,m,k;

int get(int x)
{
	queue<PII > q;
	if(x&1) q.push({x,x}); //这一层的左右区间,奇数就自己
	else q.push({x,x+1}); //偶数是两个
	int sum = 0;
	while(q.size())
	{
		auto now = q.front();q.pop();
		sum += min(n,now.se)-now.fi+1; //这段区间可以加多少数
		if(now.fi*2<=n) q.push({now.fi*2,now.se*2+1}); //如果还可以扩展
	}
	return sum;
}

bool check(int mid)
{
	int sum = get(mid);
	return sum>=k;
}

signed main()
{
	IOS;
	cin>>n>>k;
	int maxn = 0;
	int l = 1,r = n&1?n:n-1; //初始范围
	while(l<=r) //l<=r
	{
		int mid = (l+r)>>1;
		if(mid%2==0) mid--; //如果不是奇数-1
		if(check(mid))
		{
			l = mid+2; //+2
			maxn = max(maxn,mid); //答案要在这里面更新
		}
		else r = mid-2; //-2
	}
	l = 2,r = n&1?n-1:n;
	while(l<=r)
	{
		int mid = (l+r)>>1;
		if(mid%2!=0) mid--;
		if(check(mid))
		{
			l = mid+2;
			maxn = max(maxn,mid);
		}
		else r = mid-2;
	}
	cout<<maxn;
	return 0;
}

总结:
多多思考,画图模拟模拟。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值