题解:P17063 [JRKSJ R10 热身赛] Nelumbo nucifera
题很有意思!赞美出题人!
把 \(n\) 种颜色分块,块长设为 \(B\)。
预处理 \(p_1(i)\) 表示该节点到根节点的路径上,包含除了第 \(i\) 块以外的所有颜色,每种颜色恰好出现一次;\(p_2(i,j)\) 表示该节点到根节点的路径上,包含除了第 \(i,j\) 块以外的所有颜色,每种颜色恰好出现一次。
查询 \(x,y\) 时,设 \(x,y\) 所在的块分别为 \(b_x,b_y\)。若 \(b_x=b_y\),则从 \(p_1(b_x)\) 开始接上块内的其他颜色;若 \(b_x\neq b_y\),则从 \(p_2(b_x,b_y)\) 开始接上两个块内的其他颜色。
暴力预处理的次数是 \(\mathcal{O}\left(\dfrac{n^3}{B^2}\right)\),查询的总次数是 \(\mathcal{O}(nB)\)。取 \(B=n^{2/3}\) 可以做到 \(\mathcal{O}(n^{5/3})\) 次。
瓶颈在于预处理,考虑优化 \(p_2\) 的次数。仿照缺一分治,分治到 \([l,r]\) 表示节点 \(u\) 到根节点的路径上,包含除了 \([l,r]\) 块以外的所有颜色,每种颜色恰好出现一次。若两个块都在 \([l,mid]\) 内,就把 \([mid+1,r]\) 块内的颜色加到 \(u\) 中递归下去;两个块都在 \([mid+1,r]\) 内同理;若一个块在 \([l,mid]\) 内,另一个块在 \([mid+1,r]\) 内,可以在 \([l,mid]\) 和 \([mid+1,r]\) 内依次分治。具体来说,分治时维护两个区间 \([l_1,r_1]\) 和 \([l_2,r_2]\),先对左区间折半,再对右区间折半……这样交替分治下去,直到两个区间的长度都为 \(1\)。每次折半时都把另一侧的颜色加到 \(u\) 上。
分析操作次数。对于交替分治,设 \(T_1(n)\) 表示左右两个分治区间的长度都为 \(n\) 时,需要添加多少个块,则
解得 \(T_1(n)=3n^2-3n\)。
对于普通的分治,设 \(T_2(n)\) 表示当前分治区间的长度为 \(n\) 时,需要添加多少个块,则
设 \(n=2^k\),则 \(T_2(n)=\dfrac{3n^2-(k+5)n}{2}\)。
于是我们把 \(p_2\) 的预处理优化到了 \(T_2\left(\dfrac nB\right)B=\mathcal{O}\left(\dfrac{n^2}{B}\right)\) 次。\(p_1(i)\) 也可以从 \(p_2(i,j)\) 出发得到,操作次数为 \(\mathcal{O}(n)\)。
查询部分次数不变,取 \(B=n^{1/2}\),可以做到 \(\mathcal{O}(n^{3/2})\) 次。可以通过本题。
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using i128 = __int128;
using ui = unsigned int;
using ull = unsigned long long;
using u128 = unsigned __int128;
using ld = long double;
using pii = pair<int, int>;
const int MAXN = 5e4 + 5;
const int LEN = 200, MAX_CNT = (MAXN - 5) / LEN + 5;
template<typename T> T lowbit(T x) { return x & -x; }
template<typename T> void chkMin(T &x, T y) { x = y < x ? y : x; }
template<typename T> void chkMax(T &x, T y) { x = x < y ? y : x; }
constexpr int lg2(ll x) { return 63 ^ __builtin_clzll(x); }
constexpr ll bitCeil(ll x) { return x == 1 ? 1ll : 1ll << lg2(x - 1) + 1; }
int n, len;
int p1[MAX_CNT], p2[MAX_CNT][MAX_CNT];
bool flag;
int blCnt, blNum[MAXN], lb[MAX_CNT], rb[MAX_CNT];
int add(int id, int col);
void solve1(int p, int l1, int r1, int l2, int r2, int tp) {
if (l1 == r1 && l2 == r2) {
p2[l1][l2] = p;
return;
}
if (!tp) {
int mid = l1 + r1 >> 1;
int u = p;
for (int i = lb[mid + 1]; i <= rb[r1]; ++i) u = add(u, i);
solve1(u, l1, mid, l2, r2, tp ^ 1);
u = p;
for (int i = lb[l1]; i <= rb[mid]; ++i) u = add(u, i);
solve1(u, mid + 1, r1, l2, r2, tp ^ 1);
} else {
int mid = l2 + r2 >> 1;
int u = p;
for (int i = lb[mid + 1]; i <= rb[r2]; ++i) u = add(u, i);
solve1(u, l1, r1, l2, mid, tp ^ 1);
u = p;
for (int i = lb[l2]; i <= rb[mid]; ++i) u = add(u, i);
solve1(u, l1, r1, mid + 1, r2, tp ^ 1);
}
}
void solve2(int p, int l, int r) {
if (r - l + 1 == 2) {
p2[l][r] = p;
return;
}
int mid = l + r >> 1;
int u = p;
for (int i = lb[mid + 1]; i <= rb[r]; ++i) u = add(u, i);
solve2(u, l, mid);
u = p;
for (int i = lb[l]; i <= rb[mid]; ++i) u = add(u, i);
solve2(u, mid + 1, r);
solve1(p, l, mid, mid + 1, r, 0);
}
void init(int n_) {
n = n_;
for (int i = 1, j = 1; j <= n; ++i) {
lb[i] = j;
rb[i] = min(j + LEN - 1, n);
while (j <= rb[i]) blNum[j++] = i;
}
blCnt = blNum[n];
if (blCnt >= 2) {
int mx = bitCeil(blCnt);
for (int i = blCnt + 1; i <= mx; ++i) {
lb[i] = n + 1;
rb[i] = n;
}
solve2(1, 1, mx);
for (int i = 1; i <= blCnt; ++i) {
int j = i == blCnt ? 1 : i + 1, u = p2[min(i, j)][max(i, j)];
for (int k = lb[j]; k <= rb[j]; ++k) u = add(u, k);
p1[i] = u;
}
} else {
p1[1] = 1;
}
}
int solve(int n, int x, int y) {
if (!flag) {
flag = true;
init(n);
}
int bx = blNum[x], by = blNum[y];
if (bx > by) swap(bx, by);
if (bx == by) {
int u = p1[bx];
for (int i = lb[bx]; i <= rb[bx]; ++i)
if (i != x && i != y)
u = add(u, i);
return u;
} else {
int u = p2[bx][by];
for (int i = lb[bx]; i <= rb[bx]; ++i)
if (i != x && i != y)
u = add(u, i);
for (int i = lb[by]; i <= rb[by]; ++i)
if (i != x && i != y)
u = add(u, i);
return u;
}
}

浙公网安备 33010602011771号