LCS/LIS 的若干拓展
咕咕咕
题目放上来:P9109, P2075, SP12076(loj6564)
\(\mathcal{O}(\frac{nm}{w})\) 的序列 LCS
序列 LCS 最优是什么复杂度?\(\mathcal{O}(nm)\) 吗?不,其实是 \(\mathcal{O}(\frac{nm}{w})\)。
首先 \(f_{i, j}\) 表示 \(s_{[1, i]}\) 和 \(t_{[1, j]}\) 的 LCS,那么 \(f_{i, j} = \max(f_{i - 1, j}, f_{i, j - 1}, [s_i = t_j]f_{i - 1, j - 1} + 1)\)。所以 \(0 \le f_{i, j} - f_{i - 1, j} \le 1\),于是我们可以对于每个 \(j\) 维护 \(f_{i, j}\) 的差分序列,这个可以用一个 bitset 维护。
怎么转移呢,记 \(g_i\) 表示每个位置 \(i\) 是否出现,也用一个 bitset 维护。那么对于 \(f_{j - 1}\),我们将其分成若干个 000...1 的块,最后面有个 000...00 的块。那么对于一个块,把其对应位置的 \(g\) 的情况也拿出来,假设为 0100...0,那么把 \(g\) 这一块最前面的 \(1\) 取出放到 \(f\) 这一块里,并把 \(f\) 这一块的最后一个 \(1\) 变成 \(0\),因为假设 \(g\) 这一块最前面的 \(1\) 的位置为 \(x\),则 \(f_x \leftarrow f_x + 1\),于是差分数组上的末位要变成 \(0\),因为 \(f_x\) 和末位的 \(f\) 值一样大了。
那其实就是令 \(h = f | g\),然后将 \(f\) 的每个块拎出来,取出 \(h\) 在这一块中的 lowbit 放到新的 \(f\) 里。对于 lowbit,我们有公式 \((x \oplus (x - 1)) \& x\),证明显然,于是对于每一块,我们都要将其减一再和自己 \(\&\) 一下。那么由于上一块的结尾一定是 \(1\),所以我们可以把上一块的结尾移到这一块的开头,然后减掉即可,由于第一块前面没有东西,所以默认 \(f\) 最前面有个 \(1\)。然后这不就是 \(f \leftarrow 2f + 1\) 吗!于是我们得到:
由于有减法,要手写 bitset。
时间复杂度 \(\mathcal{O}(\frac{nm}{w})\)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef __int128 i128;
typedef pair<int, int> pii;
const int N = 7e4 + 10, M = 1095, mod = 998244353;
template<typename T>
void dbg(const T &t) { cout << t << endl; }
template<typename Type, typename... Types>
void dbg(const Type& arg, const Types&... args) {
cout << arg << ' ';
dbg(args...);
}
namespace Loop1st {
int n, m, a[N], b[N];
struct Bitset {
ull v[M + 1];
void operator <<= (int x) {
ull las = 0;
for (int i = 0; i <= M; i++) {
ull now = v[i] >> (64 - x);
v[i] = v[i] << x | las;
las = now;
}
}
int count() {
int res = 0;
for (int i = 0; i <= M; i++) res += __builtin_popcountll(v[i]);
return res;
}
} f, g[N];
Bitset operator ^ (Bitset A, Bitset B) {
for (int i = 0; i <= M; i++) A.v[i] ^= B.v[i];
return A;
}
Bitset operator & (Bitset A, Bitset B) {
for (int i = 0; i <= M; i++) A.v[i] &= B.v[i];
return A;
}
Bitset operator | (Bitset A, Bitset B) {
for (int i = 0; i <= M; i++) A.v[i] |= B.v[i];
return A;
}
Bitset operator - (Bitset A, Bitset B) {
ull borrow = 0;
for (int i = 0; i <= M; i++) {
ull tmp = A.v[i] < B.v[i] + borrow ? 1 : 0;
A.v[i] -= B.v[i] + borrow;
borrow = tmp;
}
return A;
}
void main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= m; i++) cin >> b[i];
for (int i = 1; i <= n; i++) {
g[a[i]].v[i >> 6] |= (1llu << (i & 63));
}
for (int i = 1; i <= m; i++) {
Bitset h = f | g[b[i]];
f <<= 1;
f.v[0] |= 1;
f = (h ^ (h - f)) & h;
}
cout << f.count() << '\n';
}
}
int main() {
// freopen("data.in", "r", stdin);
// freopen("data.out", "w", stdout);
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T = 1;
// cin >> T;
while (T--) Loop1st::main();
return 0;
}
// start coding at 19:07
// finish debugging at 19:50
区间 LIS
我们声称区间 LIS 可以做到 \(\mathcal{O}(n \log^2 n)\)。具体见 link1 和 link2。但是这里给出的是 \(\mathcal{O}(n \sqrt{n} \log n)\) 的做法。
考虑 LIS 的求法,DP 显然很倒闭,另外一种是 \(f_i\) 表示 LIS 为 \(i\),结尾的数最小是多少,加入一个数 \(x\),则转移为 \(\forall f_i < x, f_{i + 1} \leftarrow \min(f_{i + 1}, x)\)。这就相当于,每次找到最小的 \(> x\) 的数,然后让 \(x\) 代替它的作用。
刻画一下这个过程,考虑一个初始为空的集合 \(S\),每次加入一个数 \(x\),如果 \(S\) 中没有比 \(x\) 更大的数,就让 \(S \leftarrow S \cup \{x\}\),否则将最小的 \(> x\) 的数修改为 \(x\)。则 LIS 就是最后 \(S\) 的大小。
设区间 \([l, r]\) 进行这个过程得到的集合为 \(S_{l, r}\),有如下性质:
- \(S_{l, r} \subseteq S_{l - 1, r}\)
- \(|S_{l - 1, r}| - |S_{l, r}| \le 1\)
证明是显然的,你考虑一开始令 \(S_{l, r} = \varnothing, S_{l - 1, r} = p_{l - 1}\),然后对 \([l, r]\) 进行这个过程,就发现是对的了。
那么 \(r\) 固定时,对于 \(x\),一定有一个前缀 \(l\) 满足 \(\forall i \le l, x \in S_{i, r}, \forall i > l, x \notin S_{i, r}\),记这个 \(l\) 为 \(a_x\),那么询问就是问 \(a_x \ge l\) 的 \(x\) 有多少个。考虑对 \(r\) 扫描线,\(r \rightarrow r + 1\) 时,记 \(v = p_{r + 1}\),\(a_v\) 显然会变为 \(r + 1\),且一开始有一个变量 \(x = 0\),对于 \(i = v + 1, v + 2, \dots, n\) 依次考虑,如果 \(a_i > x\) 则 \(a_i\) 和 \(x\) 交换,这就是模拟 \(S\) 中加了一个 \(v\) 后的改变。
然后发现这不是我们 P14400 吗。直接改改就过了,时间复杂度 \(\mathcal{O}(n \sqrt{n} \log n)\)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
// typedef __int128 i128;
typedef pair<int, int> pii;
const int N = 1e5 + 10, B = 317, mod = 998244353;
template<typename T>
void dbg(const T &t) { cout << t << endl; }
template<typename Type, typename... Types>
void dbg(const Type& arg, const Types&... args) {
cout << arg << ' ';
dbg(args...);
}
namespace Loop1st {
int n, Q, L[B], R[B], bel[N], cnt, p[N], a[N], ans[N];
priority_queue<int>q1[B];
priority_queue<int, vector<int>, greater<int>>q2[B];
struct Query {
int l, r, id;
bool operator < (const Query &A) const {
return r < A.r;
}
} q[N];
struct Fenwick {
int tr[N];
void add(int x, int v) {
while (x) tr[x] += v, x -= x & -x;
}
int ask(int x) {
int res = 0;
while (x <= n) res += tr[x], x += x & -x;
return res;
}
} BIT;
void rebuild(int id) {
auto &pq = q2[id];
if (pq.empty()) return ;
for (int i = L[id]; i <= R[id]; i++) if (a[i] > pq.top()) {
pq.push(a[i]);
a[i] = pq.top();
pq.pop();
}
priority_queue<int, vector<int>, greater<int>>().swap(pq); // <=> pq.clear() 虽然 pq 没有 clear() 函数
}
int query(int l, int r, int x) {
int bl = bel[l], br = bel[r];
if (bl == br) {
rebuild(bl);
for (int i = l; i <= r; i++) if (a[i] > x) swap(a[i], x);
priority_queue<int>(a + L[bl], a + R[bl] + 1).swap(q1[bl]); // 即令 q1[bl] = {a[L[bl]], a[L[bl] + 1], ..., a[R[bl]]}
return x;
}
rebuild(bl); rebuild(br);
for (int i = l; i <= R[bl]; i++) if (a[i] > x) swap(a[i], x);
priority_queue<int>(a + L[bl], a + R[bl] + 1).swap(q1[bl]);
for (int i = bl + 1; i < br; i++) {
if (x >= q1[i].top()) continue;
q2[i].push(x);
q1[i].push(x);
x = q1[i].top();
q1[i].pop();
}
for (int i = L[br]; i <= r; i++) if (a[i] > x) swap(a[i], x);
priority_queue<int>(a + L[br], a + R[br] + 1).swap(q1[br]);
return x;
}
void main() {
cin >> n >> Q;
cnt = (n + B - 1) / B;
for (int i = 1; i <= cnt; i++) {
L[i] = R[i - 1] + 1;
R[i] = i * B;
}
R[cnt] = n;
for (int i = 1; i <= n; i++) {
bel[i] = (i + B - 1) / B;
cin >> p[i];
q1[bel[i]].push(0);
}
for (int i = 1, l, r; i <= Q; i++) {
cin >> l >> r;
q[i] = {l, r, i};
}
sort(q + 1, q + Q + 1);
// 0 永远不会被 ask 到,所以不用 BIT 维护 0
for (int r = 1, now = 1; r <= n; r++) {
int v = p[r];
if (v < n) {
int x = query(v + 1, n, 0);
BIT.add(x, -1);
}
int b = bel[v];
rebuild(b);
BIT.add(a[v], -1); a[v] = r; BIT.add(a[v], 1);
priority_queue<int>(a + L[b], a + R[b] + 1).swap(q1[b]);
while (now <= Q && q[now].r == r) ans[q[now].id] = BIT.ask(q[now].l), now++;
}
for (int i = 1; i <= Q; i++) cout << ans[i] << '\n';
}
}
int main() {
// freopen("data.in", "r", stdin);
// freopen("data.out", "w", stdout);
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T = 1;
// cin >> T;
while (T--) Loop1st::main();
return 0;
}
// start coding at 10:20
// finish debugging at 10:27

浙公网安备 33010602011771号