IOI2025集训队互测 W2

Day4(20241022)

考场上想到了 T1 的 \(60\%\) 的前缀和 \(20\%\) 的后缀,但是中间那一步不会;T2 貌似是很厉害的背包结论,我不会,但是赛时数据被简单做法爆掉了,难过。

感觉三道题都是很厉害的题目,我懂得欣赏。但是 T3 卡我常,难过。

T1 Désive

首先我们考虑如何快速求出 \(\operatorname{xormex}\),发现我们可以在 Trie 树上 DP,某一个节点的 DP 值 \(f_i\) 如下:有一个子树是满的(也就是 \(f_{ls}=2^k\)\(f_{rs}=2^k\)),那么令 \(f_i=f_{ls}+f_{rs}\);否则 \(f_i=max(f_{ls},f_{rs})\)

或者,从另一个角度考虑,从每一个 Trie 树上的叶子节点开始向上跳父亲,每当遇到一个满的兄弟节点,就加上它的大小。所有数中最大的就是 \(\operatorname{xormex}\)

然后套用莫队可以做到 \(O(2^nn\sqrt{m})\) 解决 \(o=1\)

发现要求子区间权值和的问题,所以尝试使用离线扫描线,然后套用历史和。我们考虑在 \(r\) 指针逐渐移动的过程中,实时维护对于每一个左端点的 \(\operatorname{xormex}\)

先考虑 \(a_i\) 互不相同的情况,我们仍然维护一个 Trie 树上的 DP,那么 Trie 树上的每一个节点都只会被填满依次。根据上面的 DP,我们知道每当其被填满依次,它就会影响一部分的 DP 值,具体的就等于它对面的子树大小,发现加起来是 \(O(n2^n)\) 的,可以接受。但考虑它还需要在往上跳的过程中可以和某些满的兄弟树合并,这一部分可以使用类似归并的方式使得每一次只增加一个修改。这样总共的新增 \(\operatorname{xormex}\) 数量只会有 \(O(2^nn)\) 个。

我们维护出上面每个情况需要用到的编号最小的点,那么需要做的操作就是前缀取 \(\max\),历史和。发现由于只有前缀取 \(\max\),所以序列是单调的,所以可以变成区间加区间历史和。

考虑如果有 \(a_i\) 相同,那么一个子树被填满的时刻可能不止依次,导致复杂度可能劣化到 \(O(2^{2n})\),但是我们考虑到,如果将这个子树填满是从 \(v\) 到了 \(v'\),我们实际上需要考虑的只是兄弟子树中所有时序在 \([v,v']\) 之间的 \(\operatorname{xormex}\) 方案。

由此,我们可以对每一个节点建立一个求解 \(\operatorname{xormex}\) 的 Trie 树,那么每一次取出了值之后,将时序最早的那一个点删掉。而修改一个点只会在 \(O(n)\) 个 Trie 树里面加入,所以维护这一部分的复杂度不会超过 \(O(2^nn^2)\),得到的修改只会有 \(O(2^nn)\) 次。

使用区间加历史和线段树,时间复杂度为 \(O(2^nn^2+mn)\)

T2 分道扬镳

\(v=\max\limits_{i=1}^nv_i,w=\max\limits_{i=1}^nw_i\)

我们先考虑如何处理无价值背包(转化题意后也就是 \(k=1\) 的部分分):

\(n\) 个物品,第 \(i\) 个物品的重量为 \(v_i\),问能够选择若干个物品,使得重量和为 \(m\)

显然存在一个 \(O(n^2v)\) 的直接 DP 的方式,即 \(f_{i,j}\) 表示前 \(i\) 的物品重量和为 \(j\) 是否可行。

题解中给出了一个 \(O(nv)\) 的求解方式。

发现我们需要关注的部分是接近 \(m\) 的那一部分,所以我们尝试只维护重量在 \(x\in [m-2v,m]\) 这个范围内的状态,那么为了保持满足条件,我们需要在加入后面的物品的同时支持删除前面的物品。

由此,我们维护 \(g_{i,x}\) 表示前 \(i\) 个数,重量和为 \(x\) 的情况下,能够删除的编号最大的物品为多少。如果 \(g_{i,x}=-1\) 表示无法达到这个状态。

初始化就是找到最小的 \(p\) 满足 \(S=\sum\limits_{i=1}^pv_i\ge m-2v\),令 \(g_{p,S}=p\)。那么对于第 \(1\sim p\) 个物品,我们的策略是删去;对于 \(p+1\sim n\) 号物品,我们的策略是加入。那么 DP 转移如下:

  • \(g_{i,x}\to g_{i+1,x}\)
  • \(g_{i,x}\to g_{i+1,x+v_{i+1}}\)
  • 枚举 \(j\in [1,g_{i,x}]\)\(j-1\to g_{i,x-v_j}\)

发现除了最后一个转移,前面两个转移都是 \(O(nv)\) 的。而对于最后一个转移,我们观察到对于 \(j\in [1,g_{i-1,x}]\) 的部分,这个 DP 会在 \(g_{i-1,x}\) 处已经发生转移了,所以是不必要转移的,那么第三个转移的范围是 \(j\in (g_{i-1,x},g_{i,x}]\)

显然 \(g_{*,x}\) 是单调递增的,那么转移的复杂度就是 \(g_{n,x}-g_{1,x}\le n\),那么复杂度就是 \(O(nv)\)

现在我们来尝试解决原问题,发现值域很大,但是由于题意保证了 \(r-l\le k\),说明我们也可以尝试贴着上界进行 DP。

我们设计状态 \(g_{i,x,y}\) 表示前 \(i\) 个数,重量和为 \(x\),价值和为 \(y\) 的情况下,能够删除的编号最大的物品为多少。我们尝试把 \(y\) 控制在一个 \(O(k)\) 的区间 \([\alpha(x),\beta(x)]\) 之间。

发现求解 \(r\) 的方式如下:按照 \(\dfrac{w_i}{v_i}\) 从大到小排序,贪心的选择,最后一个物品如果塞不下就让他乘上一个系数 \(p\),使得其能够刚好加入。那么我们可以认为 \(r\) 是一个关于容量 \(m\) 的凸函数,也就是 \(\beta(x)\)

如果我们把物品按照 \(\dfrac{w_i}{v_i}\) 排序,那么我们发现加入后面的物品,或者删掉前面的物品,只会使得和 \(\beta(x)\) 的差距越来越大,那么直接让 \(\alpha(x)=\beta(x)-k\) 就一定合法了。

这样的情况下,DP 转移和上面几乎一致,时间复杂度是 \(O(nvk)\) 的,使用滚动数组之后空间复杂度为 \(O(n+vk)\)

T3 观虫我

我们可以使用 \(64\) 位的 unsigned long long 去压缩 \(a\) 数组的存储,可以优化一定的常数。

发现存在如下的三种做法:

  1. 在每一次翻转的时候,翻转所有基于 \(x\) 将任意个 \(0\) 变成 \(1\) 对应的位置。
  2. 在每一次查询的时候,查询所有 \(x\) 将任意个 \(1\) 变成 \(0\) 对应的位置。
  3. 在每一次反转的时候,翻转所有基于 \(x\) 将任意个 \(0\) 变成 \(1\) 对应的位置,查询所有 \(\bar{x}\) 将任意个 \(1\) 变成 \(0\) 对应的位置。

发现就是对于每一位的贡献矩阵 \(\begin{bmatrix}1&1\\0&1\end{bmatrix}\) 的三种拆分方式:\(\begin{bmatrix}1&1\\0&1\end{bmatrix}\times \begin{bmatrix}1&0\\0&1\end{bmatrix}\)\(\begin{bmatrix}1&0\\0&1\end{bmatrix}\times \begin{bmatrix}1&1\\0&1\end{bmatrix}\)\(\begin{bmatrix}1&0\\1&1\end{bmatrix}\times \begin{bmatrix}1&1\\1&0\end{bmatrix}\)

发现每一位是独立的,那么我们就可以尝试找到每一种对于每一位分配方式 \(A,B,C\),使得需要进行的操作数最小。

对于翻转操作 \(x\),需要操作的位数为 \(|(A\cap \bar{x})\cup(C\cap x)|\);对于查询操作,需要操作的位数为 \(|(C\cap \bar{x})\cup(B\cap x)|\)。而某一种方案的操作数就是所有 \(2\) 的操作位数次方之和。题解中证明了随机选取 \(A,B,C\),期望操作数为 \(O(\left(\frac{4}{3}\right)^n)\) 的,加上 unsigned long long 的压位优化就可以做到 \(O(\left(\frac{4}{3}\right)^{n-\log \omega})\) 的复杂度。

但是很可惜我写的代码可能因为常数问题无法通过,是我不懂得欣赏了。

Day5(20241025)

今天 T1 发现神秘 \(O(n\log n)\) 做法,貌似暴标了。T2 感觉和正解有一定的距离,但是不远。

T1 长野原龙势流星群

发现有一个很显然的二分答案做法,我们先二分答案 \(X\),然后把 \(i\) 点的权值变成 \(w_i-X\),就是检验是否存在以这个点为根的权值和 \(\ge 0\) 的连通块了。这个问题是有一个很显然的贪心的的:\(f_i\) 表示以 \(i\) 为根的答案,那么就有 \(f_i=w_i-X+\sum\limits_{y\in son(i)}\max(f_v,0)\),可以认为是它只选择那些 \(>0\) 的儿子。

但是发现二分没有前途,所以我们考虑对于 \(X\) 进行扫描,直接从大到小,那么所有点的权值也都从 \(w_i-X\) 开始从小往大增大。

如果我们能在这个过程中动态维护所有的 \(f_i\),那么我们就只需要关注每一个 \(f_i\) 变成 \(0\) 的时刻的 \(X\) 即可。

而对于一个 \(f_i\),其变成 \(0\) 之后,随着 \(X\) 的增大,其必然一直保持 \(f_i>0\),那么在其父亲的决策中就必然会选择这个点。那么我们就可以让其和其父亲所在的连通块合并。

那么我们就变成了合并树上的两个连通块,以及查询当前所有 \(<0\)\(f_i\) 中最早变成 \(0\)\(f_i\)(也就是找最小的 \(\dfrac{f_i}{siz_i}\))。发现可以直接使用并查集和可删堆分别处理,时间复杂度 \(O(n\log n)\)

T2 Classical Counting Problem

发现如果确定了 \(min\)\(max\) 的具体值,这个连通块就是确定的:首先需要将 \(min\)\(max\) 联通,然后依次将和当前连通块相邻的点 \(v\in[min,max]\) 的数加入连通块知道不存在为止。

\(S(x)\) 表示以 \(x\) 为最小值的极大连通块,\(T(x)\) 表示以 \(x\) 为最大值的极大连通块。那么如果 \(min\in T(max)\)\(max\in S(min)\),那么意味着 \(min\)\(max\) 联通,且连通块大小为 \(|S(min)\cup T(max)|\)

而发现 \(S(x)\)\(T(x)\) 的结构是可以被写成树形的,具体的,每一次选出连通块的最小/大值,然后将这个点删去,对每一个小连通块分别处理即可。

对于一个 \(min=u\),考虑我们要求什么:\(u\times \sum\limits_{u\in T(v),v\in S(u)}v\times (T(v) \text{中在} S(u) \text{子树内的节点数量})\)

发现对 \(T\) 的构造树进行树链剖分,可以将求值变成如下数据结构问题:

  1. 单点修改 \(a_i\)
  2. 区间 \(b_i\pm 1\)
  3. 查询区间 \(a_i\times b_i\) 和。

这个是可以直接使用线段树维护的。而查询每一个 \(u\) 的答案,相当于要得到在 \(T\) 树上只点亮 \(u\) 子树内的所有节点的结果,这个部分是可以使用 DSU on tree 的。

最终复杂度为 \(O(n\log^3n)\)

T3 运筹帷幄

会做法了,但是感觉写起来太困难了。

首先我们考虑如何对于一个点去求解答案。发现我们有如下贪心:dfs 整棵树,处理 \(x\) 前先依次处理它的每一个儿子,不断将 \(x\) 子树内最深的点移动到 \(x\) 直到其他子树没有节点或者 \(x\) 处被填满了。

那么尝试使用换根来实现对每一个点处理这个过程。我们尝试维护一个数组,其中 \(f_i\) 表示距离当前节点为 \(i\) 的棋子数量,那么每一次操作都可以抽象成取出后 \(k\) 大然后插到最前面。

考虑从一个点 \(x\) 到它的儿子的时候,发现轻儿子可以利用 DSU on tree 的结论保证复杂度,所以需要想一个办法处理重儿子。我们不妨单独将重儿子的那些点取出来,只需要维护这些点的后多少个被删掉了即可。

这样我们就得到了一个如下的做法:我们对于每一个点维护出数组 \(f_{x,i}\) 表示 \(x\) 子树内处理完之后距离 \(x\) 深度为 \(i\) 的点的数量。那么 \(x\) 的数组可以从重儿子 \(bson_x\) 处继承过来,轻儿子直接暴力合并即可。如果需要还原重儿子的数组,可以直接撤销。

接下来在换根的过程中,维护一个散点数组 \(F\),以及一个集合 \(S\),其中 \(S\) 就是存储 dfs 过程中加入的重儿子数组编号。

当我们访问 \(x\) 节点的时候,先将所有轻儿子的点加入 \(F\) 中。然后考虑继续遍历他的儿子 \(y\)

  • 如果 \(y\) 是重儿子,就不需要进行任何操作;
  • 如果 \(y\) 是轻儿子,就将重儿子对应的数组加入 \(S\) 中,然后将 \(F\) 中这个轻儿子子树的点删掉。

每一次要做取出后 \(k\) 大的操作时,可以直接在 \(F\)\(S\) 中的数组上一起二分,由于 \(S\) 中只有 \(O(\log n)\) 个数组,所以复杂度是 \(O(n\log^2n)\) 的。

如果我们每一次二分只需要在 \(S\) 中的一个数组上进行,那么我们的复杂度就可以被优化到 \(O(n\log n)\)

所以我们考虑每一次想 \(S\) 中加入数组的时候,只截取 \(>size_y\) 的部分,对于 \(\le size_y\) 的部分直接加入 \(F\) 的复杂度是正确的。

此时加入的数组区间必然是 \((size_y,size_x)\) 的部分,那么所有 \(S\) 中的数组都不相交,那么每次只需要二分其中一个即可。这样时间复杂度为 \(O(n\log n)\)

Day6(20241027)

感觉今天的题比较简单。

T1 树数叔术

先考虑无标号树。

可以观察到,树上不可能存在两个权值为 \(0\) 的节点,因为如果这样对于其中任意一个点 \(rt\) 来说,\(S=\{rt\}\) 就不满足条件了。所以权值为 \(0\) 的点有且仅有一个,我们令其为根。

现在考虑只包含权值为 \(0,1\) 的点构成的虚树(那些由于构建虚树加入的点被认为无权值),其上如果存在两个连通块 \(S_1,S_2\)(不妨假设 \(|S_1|\le |S_2|\)),有 \(\operatorname{mex}(S_1)=\operatorname{mex}(S_2)=2\),那么显然有 \(x\in S_2\setminus S_1\) 使得 \(a_x\le 1\),那么就有 \(\min(E\setminus S_1)\le 1\),与题目要求矛盾。

发现这个结论可以任意拓展到任意的 \(0,1\dots i\) 构成的虚树。这也就意味着,每当我增加一个权值之后,虚树上满足 \(\operatorname{mex}\)\(i+1\) 的连通块唯一,也就是整个虚树。

那么发现我们从 \(0,1\dots i-1\) 的树加入权值为 \(i\) 的节点的过程只能有两种情况:

  1. 所有的 \(i\) 都在虚树的树边上或是节点。
  2. 加入一个作为叶子的 \(i\)

由此我们可以设计如下 DP,\(f_{i,j,k}\) 表示已经处理了权值为 \(0,1\dots i\) 的点,虚树大小为 \(j\),其中无权值点个数为 \(k\) 的方案数。

对于第一种转移,我们假设在树边上加入 \(V\) 个点,将 \(l\) 个无权点变成权值为 \(i+1\) 的,那么就有 \(f_{i+1,j+V,k-l}\gets f_{i,j,k}\times \dbinom{k}{l}\times \dbinom{V+j-2}{j-2}\),注意 \(V=0,l=0\) 以及 \(V\neq 0,j=0\) 的转移时不合法的,需要特判掉。发现 \(V\)\(l\) 的转移是相对独立的,可以分别进行转移以节省复杂度。

对于第二种转移,我们发现这个叶子有两种情况:挂在某一个节点上,或在某一条虚边上加入一个虚点然后挂在这个虚点上。对于第一种情况,有转移 \(f_{i+1,j+1,k}\gets f_{i,j,k}\times j\);对于第二种情况,有转移 \(f_{i+1,j+2,k+1}\gets f_{i,j,k}\times (j-1)\)

最终答案为 \(f_{V,n,0}\)。时间复杂度为 \(O(Vn^3)\),常数极小,可以直接通过。

T2 欧伊昔

发现这个问题和 FWT 很像,但是对于所有的 \(op\) 不一定都能像不进位加法来拆成三个数进行对位乘法。所以我们可以尝试拆成更多的数。

例如我们可以统计 \(0,1,2\) 出现的行数为 \(cnt_0,cnt_1,cnt_2\),那么我们有一个位数为 \(r=1+cnt_0+cnt_1+cnt_2-\max(cnt_0,cnt_1,cnt_2)\) 的方案。具体的,我们用一位存储所有的 \(9\) 个的答案,然后用 \(cnt_0+cnt_1+cnt_2-\max(cnt_0,cnt_1,cnt_2)\) 存储 \(cnt\) 较小的两个的结果。对列同理。

发现在数据随机的情况下,基本上有 \(r\le 5\),而 \(O(nr^n)\) 是一个可以接受的复杂度。

那么就可以直接通过了。

T3 人间应又雪

记从左端开始的操作为向左的,从右端开始的操作为向右的。

显然存在一个位置 \(x\),以其左侧的点为终点的操作都是向右的,以其右侧的点为终点的操作都是向左的。同时这个答案是可二分的。所以考虑二分答案 \(t\),我们假设有 \(j\) 个向右和 \(k\) 个向左的操作。

那么我们求出如下的四个数:\(pl_j,cl_j,pr_k,cr_k\)。其中 \(pl_j\) 表示,使用 \(j\) 个向右可以覆盖 \(1\sim pl_j-1\),但是无法覆盖 \(pl_j\),还剩下 \(cl_j\) 个向右没有使用。\(pr_k\)\(cr_k\) 同理。

  • 如果 \(pl_j<pr_k\),那么 \(pl_j\sim pr_k\) 无法覆盖,显然无解。
  • 如果 \(pl_j>pr_k\),那么所有的位置都已经被覆盖过了。
  • 如果 \(pl_j=pr_k=i\),如果有 \(a_i\le t+c(cl_j+cr_k)\),那么可以覆盖。

现在的问题就变成了如何求出 \(pl_j,cl_j,pr_k,cr_k\) 了,发现由于是对称的,所以我们先考虑 \(pl_j\)\(cl_j\)

对于某一对 \(j,k\) 我们考虑是要做什么。

初始的时候,每一个位置都被减了 \(t=k\),如果 \(a_i\le t\),那么可以直接跳过,不需要额外添加。

否则还需要添加 \(\left\lceil\dfrac{a_i-t}{c+1}\right\rceil\) 个向左才能补齐。

那么我们可以设计如下 DP,\(f_{i,k}\) 表示处理了前 \(i\) 位之后,有 \(k\) 个向左操作,会有 \(t=f_{i,k}\)

那么有 \(f_{0,k}=k\)\(f_{i,k}=\begin{cases}f_{i-1,k}&,a_i\le f_{i-1,k}\\f_{i-1,k}+\left\lceil\frac{a_i-f_{i-1,k}}{c+1}\right\rceil&,a_i>f_{i-1,k}\end{cases}\)

我们将 \(f_{i,k}\) 看作关于 \(k\) 的函数,也就是 \(f_i(k)\)。我们可以证明 \(f_i(k)\le f_i(k+1)\le f_i(k)+1\)

由此,我们只需要找到所有位置 \(x\),满足 \(f_i(x)=f_i(x-1)+1\),同时维护 \(f_i(0)\),那么我们就可以确定每一个 \(f\) 的值了。

那么我们考虑转移,要先找到 \(a_i=f_{i-1}(x)\),其有 \(f_i(x)=f_{i-1}(x)=a_i\);但对于 \(f_{i-1}(x')=a_i-1\),有 \(f_i(x')=a_i-1+\left\lceil\frac{1}{c+1}\right\rceil=a_i\)

那么 \(x\) 就会和 \(x'\) 合并,也就可以认为我们删掉了 \(x\) 这一个分界点。

同理,所有 \(f_{i-x}(x)=a_i-k(c+1),k=0,1\dots\),都会被合并。

然后,对于哪些 \(f_i(k)>t\) 的,我们就有 \(pl_{t-k}=i-1\)\(cr_{t-k}=t-f_{i-1}(k)\)

由此,我们可以通过扫描一遍+树状数组二分来做到 \(O((n+m)\log m)\) 求解 \(pl\)\(cl\)

发现合并的过程是和 \(t\) 无关的,这也就意味着,我们可以预处理出来每一个位置会在什么时候被删去,然后我们只需要关注还剩下哪些数,那么我们每一次删去的时候就可以求出一段 \(pl\)\(cl\) 的值了。

这个维护的容易的,但是有一定细节。

时间复杂度 \(O((n+m)\log m)\)

posted @ 2024-10-28 22:06  Xun_Xiaoyao  阅读(200)  评论(0编辑  收藏  举报
/* 鼠标点击求赞文字特效 */