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;
}