根号分治
QOJ
https://qoj.ac/contest/2908/problem/15322
题目
给定正整数 \(n\) 和 \(C\),和一个序列 \(a_1,a_2,\cdots a_n\),定义价值
你可以进行任意次操作:把任何一个 \(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 满足的三角不等式:
当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_{n+1}\)。
首先,我们让i和j之间没有耦合的项拿出来。令 \(f_i=dp_i-iC\),则 \(f_0=0\),且
现在和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位,那么
首先我们要开一个二维数组 \(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求解如下问题:
数据范围:\(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\),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曾说过:这个做法是正确的。
本文来自博客园,作者:B_F_S,转载请注明原文链接:https://www.cnblogs.com/Na2SO4/p/23242515

浙公网安备 33010602011771号