【离线+LogTrick】codeforces 1878 E. Iva & Pav
题目
https://codeforces.com/problemset/problem/1878/E
题意
输入一个正整数 \(T(1 \leq T \leq 10^4)\),代表 \(T\) 组测试用例,对于每个测试用例:
第一行,输入一个整数 \(n(1 \leq 2 \times 10^5)\)
第二行,输入长为 \(n\) 的数组 \(a(1 \leq a_i \leq 10^9)\)
第三行,输入一个整数 \(m(1 \leq m \leq 10^5)\),代表进行 \(m\) 次询问
接下来 \(m\) 行,每行输入两个正整数 \(l, k(1 \leq l \leq n, 1 \leq k \leq 10^9)\)
对于每个询问,回答出满足 \(a_l \& a_{l+1} \& … \& a_r \geq k\) 最大的 \(r\),若不可能则输出 \(-1\)。
保证:\(\sum_{i=1}^{T}{n_i} \leq 2 \times 10^5, \sum_{i=1}^{T}{q_i} \leq 2 \times 10^5\)
题解
首先,要思考如何计算 \(a_l \& a_{l+1} \& … \& a_r\)。朴素地,便是嵌套两层循环逻辑去实现,但时间复杂度将来到 \(O(n^2)\),显然不满足。那么进一步思考,有没有什么方案可以使得时间复杂度下降到可以满足的地步?
对于按位与运算,有规则:同 1 则为 1,有 0 便为 0。那么越多个数进行按位与运算,计算结果必定是单调递减的。我们不妨举个例子:
\(a_1 = 1111_2, a_2 = 1001_2, a_3 = 1000_2, a_4 = 1110_2\)
\(l = 1\)的情况:\(a_1 = 1111_2, a_2 = 1001_2, a_3 = 1000_2, a_4 = 1000_2\)
\(l = 2\)的情况:\(a_1 = 1111_2, a_2 = 1001_2, a_3 = 1000_2, a_4 = 1000_2\)
\(l = 3\)的情况:\(a_1 = 1111_2, a_2 = 1001_2, a_3 = 1000_2, a_4 = 1000_2\)
\(l = 4\)的情况:\(a_1 = 1111_2, a_2 = 1001_2, a_3 = 1000_2, a_4 = 1110_2\)
观察上述样例,容易发现的是,\(l = 1, 2, 3\) 三种得到的结果数组是完全一致的。为什么呢?
原因很简单,就是 \(a_1 \& a_2 == a_2\),所以 \(l = 1, 2\) 两种情况的结果必定相同
\(a_2 \& a_3 == a_3\),所以 \(l = 2, 3\) 两种情况的结果必定相同
若 \(a_i \& a_{i+1} \& ... \& a_j \& ... \& a_k\) 中,\(a_i \& a_{i+1} \& ... \& a_j == a_j\),那么实际上对于从 \(l = i, j\) 两种情况来说,在下标 \(j\) 之后的按位与结果都是必定相同的。
那么,我们来思考一下,发生按位与后结果不同的最多可能性有多少种?
int 型整数共有 32 位,按位与后结果不同,则至少要有一位变为 0,那么至多变 32 次。
那么,若加上这个性质,最差时间复杂度为 \(O(32n)\)。
最后将询问按照 \(l\) 从大到小,离线化处理即可。
参考代码
void solve() {
cin >> n;
for (int i = 1; i <= n; ++ i) cin >> a[i];
cin >> m;
for (int i = 0; i < m; ++ i) {
nd[i].i = i;
cin >> nd[i].j >> nd[i].k;
}
sort(nd, nd + m);
for (int i = n, j = m - 1; j >= 0; -- i) {
for (int k = i + 1; k <= n; ++ k) {
if ((a[k] & a[k - 1]) == a[k]) break;
a[k] = a[k] & a[k - 1];
}
while (j >= 0 && nd[j].j == i) {
if (a[i] < nd[j].k) ans[nd[j].i] = -1;
else {
int le = i, ri = n, md;
while (le < ri) {
md = le + ri + 1 >> 1;
if (a[md] >= nd[j].k) le = md;
else ri = md - 1;
}
ans[nd[j].i] = le;
}
-- j;
}
}
for (int i = 0; i < m; ++ i) cout << ans[i] << ' ';
cout << '\n';
}
浙公网安备 33010602011771号