[CF1375H] Set Merging
[题目链接]
http://codeforces.com/contest/1375/problem/H
[题解]
考虑对值域分块。
不难发现对于一段值域在 \([l , r]\) 中对应的也是一段连续区间。
那么不妨对每一段记 \(f_{l , r}\) 表示 \([l , r]\) 这段区间对应的节点。
考虑建出一个类似于线段树的结构进行分治 , 每次递归处理小于 / 大于 \(mid\) 的数形成的集合。最后对跨过 \(mid\) 的进行合并即可。
这样做的复杂度 \(T(B) = 2T(\frac{B}{2}) + B ^ 2 \approx O(B ^ 2)\)。
一共有 \(\frac{N}{B}\) 个块 , 故这部分复杂度 \(O(NB)\)。
那么对于每个需求只需将其对应的每块值域的节点合并起来即可。
当 \(B\) 取 \(\sqrt{N}\) 时 , 操作数约为 \(N\sqrt{N} \approx 2.2 * 10 ^ {6}\)。
[代码]
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i , l , r) for (int i = (l); i < (r); ++i)
typedef pair < int , int > pii;
#define mp make_pair
const int MN = 1e5 , MM = 2e6 , MS = 2.5e6 + 5 , MB = 1 << 8;
int A[MN] , rv[MN];
int N , M , siz , ans[MN] , B;
vector < pii > way;
inline int merge(int u , int v) {
if (!u || !v) return u + v;
way.emplace_back(mp(u , v));
return ++siz;
}
int f[MB * 2 + 5][MB + 5][MB + 5];
struct Block {
int l , r;
inline void solve(int u , int l , int r , vector < int > V) {
if (l == r) { f[u][1][1] = rv[V[1]]; return; }
int mid = l + r >> 1; vector < int > LV(1) , RV(1);
for (int i = 1; i < V.size(); ++i)
if (V[i] <= mid) LV.emplace_back(V[i]);
else RV.emplace_back(V[i]);
solve(u << 1 , l , mid , LV); solve(u << 1 | 1 , mid + 1 , r , RV);
for (int i = 1 , xl = 1 , xr = 1; i < V.size(); xl += (V[i] <= mid) , xr += (V[i] > mid) , ++i) {
for (int j = i - 1 , yl = xl - 1 , yr = xr - 1; j < V.size(); ++j , yl += (V[j] <= mid) , yr += (V[j] > mid)) {
if (j == i - 1) continue;
f[u][i][j] = merge(f[u << 1][xl][yl] , f[u << 1 | 1][xr][yr]);
}
}
}
int home[MN] , res[MB + 5][MB + 5];
inline void build() {
vector < int > vec(1);
for (int i = 1; i <= N; ++i)
if (A[i] >= l && A[i] <= r) {
vec.emplace_back(A[i]);
home[i] = home[i - 1] + 1;
} else home[i] = home[i - 1];
solve(1 , l , r , vec);
for (int i = 1; i < vec.size(); ++i)
for (int j = 1; j < vec.size(); ++j)
res[i][j] = f[1][i][j];
return;
}
inline int query(int l , int r) {
return res[home[l - 1] + 1][home[r]];
}
} blo[20];
int main() {
scanf("%d%d" , &N , &M); siz = N , B = min(N , MB);
for (int i = 1; i <= N; ++i) {
scanf("%d" , &A[i]);
rv[A[i]] = i;
}
for (int i = 1; (i - 1) * B + 1 <= N; ++i) {
blo[i].l = (i - 1) * B + 1 , blo[i].r = i * B;
blo[i].build();
}
for (int i = 1; i <= M; ++i) {
int l , r; scanf("%d%d" , &l , &r); ans[i] = 0;
for (int j = 1; j <= (N - 1) / B + 1; ++j)
ans[i] = merge(ans[i] , blo[j].query(l , r));
}
printf("%d\n" , siz);
for (auto x : way)
printf("%d %d\n" , x.first , x.second);
for (int i = 1; i <= M; ++i)
printf("%d " , ans[i]);
printf("\n");
return 0;
}