Loading

CF1375H 做题记录

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]]);
            else
                chkmin(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;
}
posted @ 2025-04-01 21:22  Sktn0089  阅读(39)  评论(1)    收藏  举报