根号分治

QOJ

https://qoj.ac/contest/2908/problem/15322

题目

给定正整数 \(n\) 和 \(C\),和一个序列 \(a_1,a_2,\cdots a_n\),定义价值

\[V_a=a_1+a_n+\sum_{i=1}^{n-1} a_i\oplus a_{i+1} \]

你可以进行任意次操作:把任何一个 \(a_i\) 改成任意一个值,代价是 \(C\)。

假设进行了p次操作,最终序列变成a‘,你需要最小化 \(pC+V_{a'}\)

数据范围:\(n\le 10^5,\sum n\le 2\times 10^5, a_i<2^{18}, C<2^{19}\)

破题

首先,我们可以证明 xor 满足的三角不等式:

\[x\oplus y+y\oplus z\ge x\oplus z \]

当y=x或y=z时可以取等。

我们令 \(a_0=a_{n+1}=0\),那么 \(V_a=\sum_{i=0}^n a_i\oplus a_{i+1}\),并且看作 \(a=(a_0,a_1,\cdots,a_{n+1})\)

所以,本质上 \(a'\) 可以看成从 \(a\) 中删去 \(p\) 个元素得到的结果。

我们考虑设计dp如下:\(dp_i\) 表示:序列 \(a_0,a_1,\cdots,a_i\) 在 \(a_i\) 不允许被删的前提下,对应的最小 \(pC+V_{a'}\)。

因此,dp转移方程如下:

\[dp_0=0\\ dp_i=\min_{0\le j\le i-1} (dp_j+x_j\oplus x_i+(i-j-1)C) \]

答案是 \(dp_{n+1}\)。

首先,我们让i和j之间没有耦合的项拿出来。令 \(f_i=dp_i-iC\),则 \(f_0=0\),且

\[f_i=\min_{0\le j\le i-1} (f_j+x_j\oplus x_i)-C \]

现在和dp本身已经没有关系了,转化成一个(4)式中间部分的查询与修改。

正向处理

我们开1个数组 mn[M],对于所有可能成为\(x_j\)的值v,\(mn[v]=\min_{x_j=v} f_j\)

修改:新加入i的信息时,把 \(mn[x_i]\) 与 \(f_i\) 取最大值,O(1)

查询:对于每个 \(v\le 2^{18}\),求出 \(mn[v]+v\oplus x_i\) 并与之前的值求最小值,O(M)

反向处理

我们已经有了一个修改 O(1) 查询 O(M) 的做法。根据套路,想有没有查询很快的方法。

嗯,我们开1个数组 mn[M],对于所有可能成为 \(x_i\) 的值w,\(mn[w]=\min_{j}(f_j+x_j\oplus w)\)

查询:对于新的 \(x_i\),\(mn[x_i]-C\) 就是答案, O(1)

修改:新加入i的信息时,对每个 w,把\(mn[w]\)改成与 \(f_i+x_i\oplus w\) 的最小值,O(M)

合并

现在我们想如何合并,使得修改和查询都是 O(512) 的呢?

对于一个数x,记 \(L(x)\) 表示低9位, \(H(x)\) 表示高9位,那么

\[x\oplus y=(L(x)\oplus L(y))+(H(x)\oplus H(y))<<9 \]

首先我们要开一个二维数组 \(mn[v][w]=\min_{H(x_j)=H(v)} (f_j+L(x_j)\oplus L(w))\)

其中 \((v>>9)\in [0,2^9-1], w\in [0,2^9-1]\)

修改:新加入i的信息时,只需要修改满足 \(H(x_i)=H(v)\) 的唯一的v,并且把所有的 \(mn[v][?]\) 和 \(f_i+L(x_i)\oplus L(w)\) 取 min

查询:对于新的 \(x_i\),我们的w的值已经确定了,是唯一的满足 \(L(x_i)=L(w)\) 的w,因此只需查询所有的 \(mn[?][w]-C\) 的最小值,就是答案

由此,我们得到了一个修改和查询都是 \(O(\sqrt M)\) 的做法,总复杂度是 \(O(n\sqrt M)\),可以通过

ARC205 Div2 E

https://atcoder.jp/contests/arc205/tasks/arc205_e

题目

给定 \(n\) 和 \(a_1,\cdots,a_n\),对于每个k求解如下问题:

\[\prod_{i=1}^k[a_i| a_k=a_k]a_i \]

数据范围:\(a_i<2^{20},n\le 4\times 10^5\)

破题

把每个点 \(a_i\) 想象成 Hypercube \(Q_{20}\) 上的点,那么我们本质上求的是,\(a_k\) 的子节点中的所有 \(a_i\) 之积

正向处理

记 \(b_v\) 表示节点 \(v\) 的所有 \(a_i\) 之积(显然,初始是1)

记 \(b_v=\prod_{L(x)=L(v),H(x)=H(v)}b_x\)

修改:加入k,只需要改 \(b_{a_k}\)

查询:对所有满足 \(v\subseteq a_k\) 的 \(v\),求\(b_v\) 的乘积。

反向处理

怎么让查询是 \(O(1)\) 呢?

记 \(f[w]\) 表示\(\prod_{x\subseteq w}b_x=\prod_{L(x)\subseteq L(w),H(x)\subseteq H(w)}b_x\)

查询:就是 \(f[a_k]\)

修改:对所有满足 \(a_k\subseteq w\) 的 \(f[w]\) 都要乘上 \(a_k\)

合并

同样的,我们可以把20位拆开,拆成高10位 \(H(x)\) 和低10位 \(L(x)\)

同样的,记 \(f[v][w]\) 表示 \(\prod_{L(x)\subseteq L(w),H(x)=H(v)} b_x\)

修改:加入 \(a_k\),也就是 \(b_{a_k}\) 需要修改,我们只需要修改满足 \({L(a_k)\subseteq L(w),H(a_k)=H(v)}\) 的 \(f[v][w]\) 就行,因为只有这些w的值会受到影响。

查询:显然只需要查所有满足 \(H(v)\subseteq H(a_k)\) 的所有v的 \(f[v][L(a_k)]\) 就行

复杂度依旧是 \(O(N\sqrt M)\),可以通过。

ABC335F

https://atcoder.jp/contests/abc335/tasks/abc335_f

题目

给定正整数 \(n\) 和 \(a_1,a_2,\cdots,a_n\)。初始时, \(a_1\) 被涂黑,其余白色,并且有一个棋子放在第1格上。

你可以重复任意次(也可以0次)这个操作:当棋子在第 \(i\) 格上时,移动到 \(i+a_i\times x\) 格并且涂黑(不允许跳到>n 的格子)。

求停止后的黑格集合有几种可能

数据范围: \(n,a_i\le 2\times 10^5\)

破题

令 \(dp_i\) 表示在第 \(i\) 格是黑色的情况下,\(i\sim n\) 格的涂色可能数量。这样,\(dp_n=1\),且:

\[dp_i=1+\sum_{x\in \mathbb{N},j=i+a_i\times x}dp_j [j\le n] \]

正向处理

修改:补上一项 \(dp_i\),O(1)

查询:以a_i为步长跳再+1,O(n/a_i)

反向处理

我们的目标是让查询 O(1),为此,对每个\(1\le d\le M\),记 \(sum[d][r]\) 表示在已经加入了 \(i+1\sim n\) 的信息之后的\(\sum_{j\in d\mathbb{Z}+r}dp_j\)

查询的时候,只要取 \(1+sum[a_i][i\mod a_i]\) 就行

修改的时候,把\(a_i\) 加入,需要修改所有的 \(sum[d][a_i\mod d]+=dp_i\),复杂度是 \(O(M)\)

不仅如此,空间 \(O(M^2)\) 也爆了。

合并

和前两个例子不一样,正向处理时我们能发现 x 越大,复杂度越低。

为此,待定一个数 \(B\),在查询时,如果 \(a_i\ge B\),就直接暴力跳,\(O(n/B)\)

如果 \(a_i<B\),我们才采用反向处理,这时我们只需要算 \(d\le B\) 的部分答案!

这样,修改时也只需要改 \(d\le B\) 的部分,

也就是 \(O(B)\) 修改 \(O(1)/O(n/B)\) 查询!

取\(B=\sqrt M\),总复杂度 \(O(n\sqrt M)\),空间复杂度 \(O(B^2)=O(M)\),可以通过。

ARC231A

https://atcoder.jp/contests/arc231/tasks/arc231_a

题目

在一个二维平面上,你在(0,0),第i个时刻会在 \((x_i,y_i)\) 冒出一个怪物。你可以选择:

  • 物理攻击:自己跑到 \((x_i,y_i)\),攻击怪物,花费移动距离平方的代价
  • 法术攻击:呆在原地,花费 \(z_i\) 代价

求打完 \(n\) 个怪物的总代价。

数据范围:\(0\le x_i,y_i<500\),\(n\le 2.5\times 10^5\),\(z_i\le 10^9\)

破题

显然,我们可以把法术攻击给去掉,最后再在答案加上 \(\sum z_i\)。这样问题变成了:

  • 物理攻击:自己跑到 \((x_i,y_i)\),攻击怪物,花费 \(dis^2-z_i\) 代价
  • 法术攻击:啥也不干

设 \(dp_{i,x,y}\) 表示我在打完i号怪物后自己在 \((x,y)\) 处的总花费最小值。

(因为这道题 \(N\sim X\times Y\),所以没必要设计自己在第j个时刻处的 \(dp[i][j]\))

查询(求解 \(dp_{i,x,y}\)):

  • 首先,\(dp_{i,x,y}=dp_{i-1,x,y}\)

  • 如果 \((x,y)=(x_i,y_i)\),那么可以使用物理攻击,就是枚举所有x',y'的 \(dp_{i-1,x',y'}+(x'-x_i)^2+(y'-y_i)^2-z_i\),求最小值

正向处理

我们发现从i-1到i,只有一个位置要修改。因此我们可以采用滚动数组优化,并且把问题转化为:

  • 先查询:所有的 \((x,y)\neq (x_i,y_i)\) 的 \(dp_{x,y}+(x-x_i)^2+(y-y_i)^2-z_i\) 的最小值
  • 再修改:把这个值和 \(dp_{x_i,y_i}\) 取min再给到 \(dp_{x_i,y_i}\)

这样,我们得到了一个经典的修改 O(1) 查询 O(XY) 的转移

反向处理

自然的,我们会想能不能做到查询 O(1)

对于当前状态,记 \(f_{x',y'}\) 表示所有的 \((x,y)\neq (x',y')\) 的 \(dp_{x,y}+(x-x')^2+(y-y')^2\) 的最小值

  • 查询:直接读取 \(min(dp_{x_i,y_i},f_{x_i,y_i}-z_i)\)
  • 修改:很明显,我们要对所有的除了 \((x_i,y_i)\) 的 \((x,y)\) 修改 f 值,具体地,\(f_{x',y'}\)和新的\(dp_{x_i,y_i}+(x_i-x')^2+(y_i-y')^2\) 取min,作为新的 \(f_{x',y'}\)

这样是修改 O(XY) 查询O(1) 的

合并

嗯,根据经验,我们会想拆开两个维度。

记 \(f_{x',y}\) 表示所有的 \(x\neq x'\) 的 \(dp_{x,y}+(x-x')^2\) 的最小值

  • 查询\((x_i,y_i)\):所有的 \(0\le y<Y\) 的 \(f_{x_i,y}\) 以及所有 \(y\neq y_i\) 的 \(dp_{x_i,y}+(y-y_i)^2\),它们能拼出需要查询的那些值。这一堆东西的最小值记为mn,再 \(min(dp_{x_i,y_i},mn-z_i)\)
  • 修改:我们要修改 \(dp_{x_i,y_i}\) 的值,并且一定会改小,这会导致所有满足 \(x'\neq x_i\) 的 \(f_{x',y_i}\) 需要修改,具体地,\(new\ f_{x',y}=min(f_{x',y},dp_{x_i,y_i}+(x-x')^2)\)

最后,在 \(i=1,2,\cdots,n\) 依次操作完后,我们要求的是全体的 \(dp_{x,y}\) 的最小值【注意再把\(\sum z_i\) 加回来!】,这个O(XY) 也不要紧,因为只做一次。

总复杂度也是: \(O(N(X+Y)+XY)\),和官方题解不一样。

五星上将Deepseek曾说过:这个做法是正确的。

posted @ 2026-10-09 19:56  B_F_S  阅读(2)  评论(0)    收藏  举报