Greater and Greater
Greater and Greater
题意:
给你一个长度为n的数组A, 和一个长度为m的数组B, 从A中找长度为 m的子串,且 子串的每一位大于等于对应的B数组的每一位, 问符合条件的子串个数。
题解:
这题 \(n*m = 6*10^9\) 所以暴力肯定不行, 所以要bitset优化暴力。 可以将复杂度降到 $\frac{n * m}{32} \approx 3 * 10 ^ 8 $
题目给的一秒, 按照一般的想法肯定是过不了, 但是牛客的机子一秒可以过 \(10 ^ 9\)所以可以稳过。
下面是解题思路:
如何用bitset操作?
先举个例子:
6 3
1 4 2 8 5 7
3 2 3
第一步 将 B数组排序:
排完序的值:2 3 3
排完序的下标:2 1 3
然后开一个 bitset<M>cnt[M]
然后
for (i = 1; i <= m; i++) {
cnt = cnt[i - 1];
cnt.set(m - B[i].pos); // 至于这里的完整为啥要取后面的,主要是为了后面 右移方便操作。
}
这样得到的 cnt就是
cnt[0] cnt[1] cnt[2] cnt[3]
000 010 011 111
也就是说这里 0 1的含义就是, 小于 B[i]的值 位置 (位置取反)
初始化完成也就是求答案了
如果让你写暴力你肯定是从第一位枚举, 上面说了这时 bitset优化的暴力同样也是从第一位枚举。
设一个 bitset<m>ans
A数组中第一值为:1
再B 找最后一个小于等于1 的值位置,
很显然是 0, 那么取cnt[0]
因为之前为值都取反那么现在一样,
ans.set(m -1)这里的意思我给第一位差个1
然后 ans = ans & cnt[0];
如果第 m- 1位是1 (即 第一位取反了)
那么可以得到 第一个数是合法的, 因为你找的cnt[0]就是 所有小于等于 A[1]值的位置。
如果第一位合法 ans >>=1将 ans右移1位, 如果你这里看懂了就知道我之前为啥把位置取反了
同理, 再找 A[2] 再B 中最后一个小于 它的位置, 我们发现是排序后的第三个, 取cnt[3],
再将 ans.set(m - 1)
ans = ans & cnt[3]
如果这个时候 第 m - 2位是1 说明第二位以合法。
以此类推, 每次至要判断 ans[0]是否等于1, 是就答案加1就行。
看代码模拟几遍即懂了。
#include<bits/stdc++.h>
using namespace std;
const int N = 150007;
int a[N], b[N], n, m, pos[N];
bitset<40007> cnt[40007], ans;
int find(int x) {
int l = 1, r = m;
int an = 0;
while (l <= r) {
int mid = (l + r) / 2;
if (b[pos[mid]] <= x) {
an = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
return an;
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
for (int i = 1; i <= m; i++) {
scanf("%d", &b[i]);
pos[i] = i;
}
sort(pos + 1, pos + m + 1, [](int x, int y) {
return b[x] < b[y];
});
for (int i = 1; i <= m; i++) {
cnt[i] = cnt[i - 1];
cnt[i].set(m - pos[i]);
}
int res = 0;
for (int i = 1; i < m; i++) {
ans.set(i, 0);
}
for (int i = 1; i <= n; i++) {
int p = find(a[i]);
ans >>= 1;
ans.set(m - 1, 1);
ans &= cnt[p];
res += ans[0];
}
printf("%d\n", res);
}