题解:P9649 [SNCPC2019] Coolbits
P9649 [SNCPC2019] Coolbits 题解
题目链接:P9649 [SNCPC2019] Coolbits
题目摘要
二进制运算 + 贪心思想:第
题目分析
乍一看感觉是和 P9612 [CERC2019] Light Emitting Hindenburg 有点类似的题。都是使得若干个数最后的按位与之和最大,不同的是本题是给定一些区间,在区间里选数;而 P9612 则是直接给定了一些数。因此本题要稍微难一些。
如果你已经做过 P9612,你就会知道这道题的大致思想是贪心。如果我们枚举最后结果的每一位,我们会发现 10000 要比 01111 要大,即
然而现在的问题是,现在不是从
具体来说,即为:
int cal(int x, int i)
{
if (!((x >> i) & 1))
x = ((x >> i) | 1) << i;
return x;
}
这个函数计算了对于每一个区间的左端点,如果它的第
一些细节
- 数据范围是
0 \leq x \leq 10^9 ,大概是int类型,即2^{31} - 1 。我们枚举答案的位数的时候就要从30 枚举到0 。 - 位运算的优先级比较容易出错,注意多加括号。
参考代码
#include <iostream>
#include <cctype>
#define ll long long
using namespace std;
int read()
{
int x = 0, f = 1;
char ch = 0;
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch))
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
const int maxN = 1e5 + 5;
int T;
int n;
ll ans;
int L[maxN], R[maxN];
int cal(int x, int i)
{
if (!((x >> i) & 1))
x = ((x >> i) | 1) << i;
return x;
}
int main()
{
T = read();
while (T--)
{
ans = 0;
n = read();
for (int i = 1; i <= n; i++)
L[i] = read(), R[i] = read();
for (int i = 30; i >= 0; i--)
{
ll d = 1ll << i;
bool flag = false;
for (int j = 1; j <= n; j++)
if (cal(L[j], i) > R[j])
{
flag = true;
break;
}
if (flag)
continue;
for (int j = 1; j <= n; j++)
L[j] = cal(L[j], i);
ans |= d;
}
printf("%lld\n", ans);
}
return 0;
}