题意

给定一个长度为 $n$ 的非负整数序列 $[a_1,…,a_n]$ ,求它的一个子序列 $[{a_i}_1,{a_i}_2,…,{a_i}_k]$ $(1≤i_1<i_2<⋯<i_k≤n)$ ,使得 $\sum^{k−1}_{j=1}({a_i}_j\text{and} {a_i}_{j+1})$ 最大。你只需要输出这个最大值即可。

$n\leq2\times10^5,0\leq a_i\leq10^{12}$

题解

挺简单的题,但思考的时间还是太长了。

一个显然的 $\mathcal{O}(n^2)$ $f_i=\max_{j<i}\{f_j+(a_i~\text{and}~a_j)\}$ ,考虑优化。这个与的二进制位的操作,让我想到类似树状数组的操作。对于 $i$ ,它选择 $j$ 的“权重”,是尽量选 $a_i~\text{and}~a_j$ 二进制最高位大的,且 $f_i$ 要大。那么对每个二进制位,维护它目前最大 $f$ ,之后的 $i$ ,如果这一位有 1 就试着选它。正确性很容易证:当前 $f$ 在第 $\text{bit}$ 位比目前最大的 $f$ 要大,那么它肯定要大 $\Omega(2^\text{bit})$ 的数量级,因为如果不如这个大,那么当前 $f$ 应该选择目前最大 $f$

复杂度 $\mathcal{O}(n\log A)$ ,其中 $A$ $a_i$ 的值域。注意考虑 $a_i~\text{and}~a_j=0$ (即全局最大)的情况。

代码

const int N = 1e6 + 5, M = 42;

inline ll Read() {
	ll x = 0, f = 1;
	char c = getchar();
	while (c != '-' && (c < '0' || c > '9')) c = getchar();
	if (c == '-') f = -f, c = getchar();
	while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
	return x * f;
}

namespace Main {
	int n;
	ll a[N], f[N], ans;
	struct node{ 
		ll val, a;
	} mx[M], nop;
	int main() {
		n = Read();
		for (int i = 1; i <= n; i++) a[i] = Read();
		for (int i = 1; i <= n; i++) {
			f[i] = max(f[i], nop.val + (nop.a & a[i]));
			for (int j = 40; ~j; --j) {
				if ((a[i] >> j) & 1) {
					f[i] = max(f[i], mx[j].val + (mx[j].a & a[i]));
				}
			}
			for (int j = 40; ~j; --j) {
				if ((a[i] >> j) & 1) {
					if (mx[j].val <= f[i]) mx[j] = (node) {f[i], a[i]};
				}
			}
			if (nop.val <= f[i]) nop = (node) {f[i], a[i]};
			ans = max(ans, f[i]);
		}
		printf ("%lld\n", ans);
		return 0;
	}
}

int main() {
//	freopen("ex_seq4.in", "r", stdin);
//	freopen(".out", "w", stdout);
	Main::main();
	return 0;
}
EOF

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。

博客信息

作者
Jayun
时间
2023-10-20 15:35:28