首页 > 技术文章 > MAX(数论)

void-f 2017-10-28 16:50 原文

Description

小C有n个区间,其中第i个区间为[li,ri],小C想从每个区间中各选出一个整数,使得所有选出的数and起来得到的结果最大,请你求出这个值。

Input Format

第一行一个正整数n,表示区间个数。接下来n行,每行两个非负整数li,ri。

\(n \leq 10^5, li \leq ri \leq 10^{18}\)

Output Format

输出一个整数,表示答案。

Solution

跟二进制有关,那么一般都要转化成二进制下操作,

我们发现,最后的答案其实是可以看成\(2^{k_1}+2^{k_2}+...2^{k_n}\),

那么其实就是选出来的数列化成二进制后第\(k_1,k_2...k_n\)位都为1,

\(k_i\)越大就越优,那么就有了思路,按二进制为从高到低验证,

如果第\(i\) 位符合条件,那么\(Ans+=2^i\) ,答案就算出来了,

当符合条件的时候,需要特殊处理否则会影响到后面的判断,

只要把所有区间都往左移动\(2^i\)即可,即减去\(2^i\),

Code

#include <cstdio>
#include <algorithm>
#define LL long long
#define N 100010
using namespace std;

int n;
LL Ans, l[N], r[N];

inline LL read() {
	LL x = 0, r = 1; char ch = getchar();
	while (ch < '0' || ch > '9') {if (ch == '-')r = -1; ch = getchar();}
	while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
	return x * r;
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i)
		l[i] = read(), r[i] = read();
	for (int k = 62; k >= 0; k--) {
		LL pk = 1ll * 1 << k;
		bool flag = 1;
		for (int i = 1; i <= n; ++i)
			if (r[i] < pk) flag = 0;
		if (flag) Ans += pk;
		for (int i = 1; i <= n; ++i) {
			if (flag || l[i] >= pk) l[i] -= pk, r[i] -= pk;
		}
	}
	printf("%lld\n", Ans);
	return 0;
}

推荐阅读