link
这题严格来说应该算是数据结构题。
这里主要记录的是值域分块的另一大特殊用法:块内分治维护位置信息,块间直接合并。
比如这一题,我们先值域分块,设块长为 \(B\)。对于一个块内,一共有 \(\mathcal O(B)\) 个数,也就是在序列上有 \(\mathcal O(B^2)\) 种不同的区间,总数为 \(\mathcal O(nB)\) 级别,所以我们可以求出所有的区间信息。
但是合并必须是值域小的与值域大的合并,所以可以考虑按值域分治。比如当前分治区间 \([l, r]\) 表示块内第 \(l\) 小的数到第 \(r\) 小的数进行分治求解,在序列上一共有 \(\mathcal O((r - l) ^ 2)\) 种本质不同的区间,先左右递归子问题,然后枚举每个本质不同的区间,其数字集合由左右两边对应的区间数字集合合并而成。
询问直接依次合并每个值域块即可。取 \(B = \sqrt q\),复杂度为 \(\mathcal O(n\sqrt q)\)。
观察这类问题的特点:
-
有两个维度
-
第一个维度需要按顺序依次合并
-
第二个维度需要限定了一个区间
按照第一个维度分块,每个块内第二个维度利用分治求解。
点击查看代码
#include <bits/stdc++.h>
#define ll int
#define ull unsigned ll
#define fi first
#define se second
#define mkp make_pair
#define pir pair <ll, ll>
#define pb push_back
#define i128 __int128
using namespace std;
char buf[1 << 22], *p1, *p2;
// #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, (1 << 22) - 10, stdin), p1 == p2)? EOF : *p1++)
template <class T>
const inline void rd(T &x) {char ch; bool neg = 0;while(!isdigit(ch = getchar()))if(ch == '-') neg = 1;x = ch - '0';while(isdigit(ch = getchar()))x = (x << 1) + (x << 3) + ch - '0';if(neg) x = -x;
}
const ll maxn = 4101, inf = 1e9, mod = 998244353, M = 22e5 + 10;
ll power(ll a, ll b = mod - 2) {ll s = 1;while(b) {if(b & 1) s = 1ll * s * a %mod;a = 1ll * a * a %mod, b >>= 1;} return s;
}
template <class T, class _T>
const inline ll pls(const T x, const _T y) { return x + y >= mod? x + y - mod : x + y; }
template <class T, class _T>
const inline void add(T &x, const _T y) { x = x + y >= mod? x + y - mod : x + y; }
template <class T, class _T>
const inline void chkmax(T &x, const _T y) { x = x < y? y : x; }
template <class T, class _T>
const inline void chkmin(T &x, const _T y) { x = x < y? x : y; }ll n, m, a[maxn], b[maxn], B, bl[maxn], id[maxn][maxn], h[maxn], tot;
vector <ll> vec[maxn]; ll f[M], g[M], _id[maxn][maxn];
ll lc[M], rc[M], ans[1 << 17], ql[1 << 17], qr[1 << 17], c[1 << 17];void solve(vector <ll> &vec, ll l, ll r) {if(l == r) return id[h[vec[l]]][h[vec[l]]] = vec[l], void();ll mid = l + r >> 1, ap = a[vec[mid]];solve(vec, l, mid), solve(vec, mid + 1, r);sort(vec.begin() + l, vec.begin() + r + 1);for(ll i = l; i <= r; i++)for(ll j = i; j <= r; j++)_id[h[vec[i]]][h[vec[j]]] = id[h[vec[i]]][h[vec[j]]];for(ll i = l; i <= r; i++) {ll l1 = n, r1 = 0, l2 = n, r2 = 0;for(ll j = i; j <= r; j++) {if(a[vec[j]] <= ap)chkmin(l1, h[vec[j]]), chkmax(r1, h[vec[j]]);elsechkmin(l2, h[vec[j]]), chkmax(r2, h[vec[j]]);if(l1 <= r1 && l2 <= r2) {++tot, lc[tot] = _id[l1][r1], rc[tot] = _id[l2][r2];id[h[vec[i]]][h[vec[j]]] = tot;}}}
}int main() {rd(n), rd(m); B = sqrt(m), tot = n;for(ll i = 1; i <= n; i++) bl[i] = (i - 1) / B + 1;for(ll i = 1; i <= n; i++) rd(a[i]), b[a[i]] = i, f[i] = g[i] = a[i];for(ll i = 1; i <= n; i++) vec[bl[i]].pb(b[i]);for(ll i = 1; i <= m; i++) rd(ql[i]), rd(qr[i]);for(ll u = 1; u <= bl[n]; u++) {auto tmp = vec[u];sort(tmp.begin(), tmp.end());for(ll i = 0; i < tmp.size(); i++) h[tmp[i]] = i;solve(vec[u], 0, vec[u].size() - 1);for(ll i = 1; i <= m; i++) {ll p = lower_bound(vec[u].begin(),vec[u].end(), ql[i]) - vec[u].begin();ll q = upper_bound(vec[u].begin(),vec[u].end(), qr[i]) - vec[u].begin() - 1;if(p > q) continue;if(c[i]) {++tot, lc[tot] = c[i], rc[tot] = id[p][q];c[i] = tot;} else c[i] = id[p][q];}} printf("%d\n", tot);for(ll i = n + 1; i <= tot; i++) printf("%d %d\n", lc[i], rc[i]);for(ll i = 1; i <= m; i++) printf("%d ", c[i]);return 0;
}