Codeforces Round #846 (Div. 2)

D. Bit Guessing Game

思路:

1,给的是n的二进制1的个数,因为n小于10^9<2^30,要求次数不超过30次,所以我们只需要从二进制0位询问到找到所有1即可(最多询问到29位(即1<<29==2^30)).

2,如果我们询问第i位是否是1,如果是,那么他给出的a(n-(1<<i))的1的个数就等于n(1的个数)-1.不是则可以保证等式一定 不成立。

3,因为我们每次减去x后,给出的是n-x的1的个数,所以我们下一次减,需要减去x'-tmp(tmp是上次减去的x),这样给出的仍然是n-x'的1的个数。

#include <bits/stdc++.h>
using namespace std;
#define ll     long long
typedef unsigned long long ull;
typedef pair<long long, long long> pll;
typedef pair<int, int> pii;

//double 型memset最大127,最小128
//std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
const int INF = 0x3f3f3f3f;         //int型的INF
const ll llINF = 0x3f3f3f3f3f3f3f3f;//ll型的llINF
const int N = 2e5 + 10;

void msolve()
{
	int n;
	cin >> n;
	int ans = 0, tmp = 0, a, cnt = 0;
	for (int i = 0; i < 30; ++i)
		{
			int x = (1 << i);
			cout << "- " << x - tmp << endl;//每次减去x-tmp,保证每次都是给出n-x的二进制1个数
			cin >> a;
			if (a == -1)return;//-1说明错误直接下一个测试案例
			if (a == n - 1)//满足说明这一位有1
				{
					++cnt;
					ans += x;
				}
			if (cnt == n)//统计完所有1就可以退出了
				{
					cout << "! " << ans << endl;
					return;
				}
			tmp = x;//更新tmp
		}
}

int main()
{
	//互动题切忌解绑,否则会最后全部输出,等于无法互动
	int t;
	cin >> t;
	while (t--)
		{
			msolve();
		}
	return 0;
}

E. Josuke and Complete Graph

思路:

我们设gcd可以取的值为d,区间为[L,R]

1,当d>R/2时,d不成立,因为至少要gcd(d,2*d)=d,而d>R/2,那么2*d>R,不在区间范围内

2,显然:L<=d<=R/2时d均成立

3,d<L时,如果可以取到d,需要满足首先有一个倍数k,使得k*d>=L并且最接近L,同时,k*d+d<=R,这样gcd(k*d,k*d+d)=d.

4,取得这个k的方法利用了向下取整的思想([L/d]*d<=L),我们要>=L,所以魔改为向上取整。

令k*d<=L-1(k*d是比L小且最接近L的,那么k*d+d就是>=L并且最接近L).

5,1e9的范围是不足以遍历L的,会被tle,这里运用整数分块的思想,我们会发现,每次k都是一个区间[l,r]里面的数i得到k相同,即[L/i]=[L/l]=[L/r]=k。所以我们可以一次处理一个区间,这样时间复杂度从O(n)变为O(根号n)。

6,我们每次从左端点l开始,如何求出右端点r呢

因为[L/i]=[L/l]=[L/r]=k,k<=L/i,  ==>  [L/k]>= [ L / [L/i] ]=[i]=i,  ==>   [L/k]=r

我们已经求出k=[L-1]/l,那么r=[L-1/k].

#include <bits/stdc++.h>
using namespace std;
#define ll     long long
typedef unsigned long long ull;
typedef pair<long long, long long> pll;
typedef pair<int, int> pii;

//double 型memset最大127,最小128
//std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
const int INF = 0x3f3f3f3f;         //int型的INF
const ll llINF = 0x3f3f3f3f3f3f3f3f;//ll型的llINF
const int N = 2e5 + 10;

int main()
{
	int t;
	cin >> t;
	while (t--)
		{
			ll L, R;
			cin >> L >> R;
			ll ans = max(0ll, R / 2 - L + 1);//首先加上L到R/2这段区间的d
			for (ll l = 1, r; l < L; l = r + 1)//遍历l从1到L-1
				{
					ll k = (L - 1) / l;
					r = (L - 1) / k;
					ans += max(0ll, (min(r, R / (k + 2))) - l + 1);//注意不一定直接加上(r-l+1),因为可能r会超区间R,所以需要比较k倍数下(k*r+r)+r(k*r+r是最接近L,k*r+r+r是右边),即r*(k+2)<=R的最大r
				}
			cout << ans << endl;
		}

	return 0;
}

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值