省选数据结构专题 1 做题记录
省选数据结构专题 1 做题记录
A CF1039D You Are Given a Tree
首先先考虑 \(k\) 给定怎么做,这有一个显然的 \(O(n)\) 贪心。我们从下往上遍历整棵树,如果一个点能够合并两条链就直接合并,否则选最长的一条链向上传递。正确性比较显然。
然后我们发现这道题中 \(k\times ans_k\le n\),这启发我们想到根号分治。对于 \(k\le \sqrt n\),暴力求出答案,复杂度 \(O(n\sqrt n)\);对于 \(k>\sqrt n\),答案一定 \(\le \sqrt n\),且由于答案单调不增,我们只需要找到不同答案的分界点即可。这个可以直接二分求解出分界点,复杂度 \(O(n\sqrt n\log n)\)。
综上我们可以在 \(O(n\sqrt n\log n)\) 的复杂度内完成这个问题。注意实现的时候需要小小卡常,可以把递归换成 DFS 序上直接遍历。
B AGC001F Wide Swap
首先发现题目中的限制比较奇特,尤其后一个限制与值域有关,考虑求出 \(P\) 的逆排列 \(Q\),这样的话我们的条件就变为:满足 \(|Q_i-Q_{i-1}|\ge k\) 的相邻的 \(Q\) 可以交换。我们最后的目标是让 \(P\) 字典序最小,那么就是让 \(Q\) 中 \(1,2,\cdots,n\) 出现的位置依次最小。
考虑这个条件说明了什么,显然这告诉我们,对于两个 \(Q_i,Q_j\),如果它们满足 \(|Q_i-Q_j|<k\),那么最后 \(Q_i\) 和 \(Q_j\) 的相对位置是不会变的。满足这个条件有一个经典套路,我们对所有 \(i<j\) 且满足上述条件的位置连边 \(Q_i\to Q_j\),这样我们求出一个拓扑序后就可以得出每一个 \(Q\) 放置的位置,而我们的目标就是让这个序列的字典序最小。
这个问题也是一个经典问题,注意这个时候我们不是让拓扑序最小,而是让拓扑编号构成的序列字典序最小。解决这个问题我们只需要建反边,然后让拓扑序最大即可。
现在我们还剩最后一个问题,我们连边的条数最多是 \(O(n^2)\) 的。解决这个问题也很简单,显然这个连边是具有传递性的,也就是说如果有 \(x\to y,x\to z(y,z\ge x)\),那么必定有 \(y\to z\)。所以对于 \(Q_j>Q_i\) 和 \(Q_j<Q_i\) 的情况,我们都只用连一个即可。所以我们只需要求出 \(i\) 右边第一个在 \([Q_i+1,Q_i+k-1]\) 和第一个在 \([Q_i-k+1,Q_i-1]\) 的两个位置连边即可。用权值线段树可以简单做到,复杂度 \(O(n\log n)\)。
C CF1578B Building Forest Trails
首先我们先断环为链,此时两条弦相交等价于两条线段相交。破开环之后我们发现序列上的线段关系比较复杂,我们考虑一个经典套路,也就是只保留相交和包含两种关系中的一种。显然此时我们应该保留包含,因为环上连通块本身就具有这个性质,即只有包含关系。
那么我们就强制要求在同一个连通块内的点按照顺序依次连边,这样的话就可以满足要求。然后考虑如何新加入一条线段。我们设两个端点是 \(x,y\),我们要知道此时这条线第一次会穿过哪一个连通块,也就是第一个包含 \(x\) 的连通块。
此时会发现这样一件事:如果我们令 \(h_i\) 表示 \(i\) 点上方经过的线段个数(不含端点),那么 \(x\) 右边第一个满足 \(h_p<h_x\) 就是这个连通块中的点。那么每一次我们实际上只需要根据 \(h_x,h_y\) 的关系,找出要移动的点,然后把两个连通块合并起来即可。特殊的情况是 \(h_x=h_y\),此时我们任意移动一个点,然后判断一下,如果还没有超出要求的范围就合并,否则的话直接合并 \(x,y\)。
合并的时候分两种情况,即包含或者无交。这个用并查集维护一下左右端点即可判断。然后按照最开始的规则连接完边之后 \(h\) 会改变,每一次会在一个区间上加一减一,用线段树维护即可。而上面查询 \(p\) 的过程显然也可以用线段树二分完成。
显然我们只有在合并的时候会调用线段树,而总共只会有 \(O(n)\) 次合并,所以总复杂度 \(O(n\log n)\)。
D CF1129D Isolation
首先我们有一个简单到爆炸的 dp:设 \(f_i\) 表示最后一段以 \(i\) 结尾的方案数,则有:
显然这是 \(O(n^2)\) 的,完全不可做。考虑这种题目的一个经典套路,我们要求区间中出现恰一次的数的个数,不难想到将每个数第一次出现的位置赋值 \(1\)、第二次出现的位置赋值 \(-1\)、后面的全部赋值 \(0\)。这样的话扫描线实时维护一下这个,要求就变为了后缀和 \(\le k\)。
那么现在我们要做的操作就是单点修改、查询后缀和 \(\le k\) 的所有位置的 dp 值之和。这个东西是没有办法用线段树等数据结构 \(\log\) 维护的,所以只能用分块。精细实现一下可以做到修改 \(O(1)\),复杂度 \(O(n\sqrt n)\),可以通过。
E AGC015E Mr.Aoki Incubator
首先先找一找题目中的性质,我们发现两个点染色后互相不会影响,那么每个人的贡献是独立的。考虑一个点染色后哪些点也会被染上,很显然左边比它快的和右边比它慢的都可以被染到;进一步的,此时右边所有染上颜色的依然可以染左边比它快的,左边也是同理。
所以如果令 \(pre_i\) 表示速度前缀最大值,\(suf_i\) 表示速度后缀最小值,那么对于一个 \(i\),所有在 \(i\) 左边且速度大于 \(suf_i\) 的都能被染上,同理所有在 \(i\) 右边且速度小于 \(pre_i\) 的都能被染上。
考虑 dp,设 \(dp_i\) 表示最后一次染色在 \(i\),\([1,i]\) 已经染完的方案数。转移的时候枚举上一次染色的点 \(j\),此时我们的条件是 \(\forall x\in(j,i),a_x\notin[pre_j,suf_i]\)。由于 \(pre_j,suf_i\) 均单调不减,所以我们知道合法的 \(j\) 一定是一段以 \(i-1\) 为结尾的区间。那么我们用桶维护一下当前 \([pre_j,suf_i]\) 中数的个数,然后双指针计算出合法的区间,用前缀和计算一下即可。复杂度 \(O(n\log n)\),瓶颈在离散化和排序。
F AGC007E Shik and Travel
首先我们不难发现,由于题目要求每条边恰走两次,所以对于任意一棵子树,我们一定是先走完子树内所有叶子再回来。考虑先二分答案,假设当前的答案是 \(ans\)。接下来考虑 dp,我们设 \(f(x,i)\) 表示走完 \(x\) 子树内所有叶子,且满足 \(x\) 到第一个叶子走的路径长 \(\le i\) 时,最后一个叶子走回 \(x\) 的路径最小值。
先考虑这个状态,我们发现,如果 \(i<i'\),那么必然有 \(f(x,i)\ge f(x,i')\),但同时如果 \(f(x,i)=f(x,i')\) 那么 \(i'\) 这个状态就是无用的。所以我们只保留 \(i<i'\) 且 \(f(x,i)>f(x,i')\) 的状态,并且认为对于 \(j\in [i,i'),f(x,j)=f(x,i)\)。我们称这些状态为有用状态。
然后考虑转移,令该点左右儿子为 \(\text{ls},\text{rs}\),边权为 \(\text{lw},\text{rw}\),分类讨论先走那个儿子即可:
直接暴力转移是 \(O(n^2)\) 的,难以通过。考虑到上文中我们提到过有用状态这个定义,不妨来考虑转移过程中 \(f(x,i)\) 的有用状态数。令 \(g(x)\) 表示 \(x\) 中的有用状态,则不难发现,\(g(x)\) 最多是 \(2\times \min(g(\text{ls}),g(\text{rs}))\);原因在于在上面的转移中,要么第一次走到较小状态的儿子中,对应的是 \(i\) 不同,要么最后一次从较小状态的儿子中走回来,对应的是 \(f(x,i)\) 值不同。所以下标和值都不同的状态最多只有较小状态数的 \(2\) 倍。
于是利用启发式合并的复杂度分析,我们容易发现此时的复杂度将至 \(O(n\log n)\),这就可以通过了。实现的时候需要精细实现转移的部分,用类似归并排序的思路可以在合理的复杂度内完成转移。总复杂度 \(O(n\log^2 n)\),可以通过。
G CF1446D Frequency Problem
\(\text{Easy Version}\),\(\text{Hard Version}\)
首先先排除全局众数有两个的情况,然后需要观察出一个比较强的性质:一定存在一种最优答案区间,满足区间内有一个众数是全局众数。
证明考虑反证,如果最优答案区间不包含全局众数,则我们可以考虑扩展这个区间。令该区间中除全局众数外众数的出现次数为 \(a\),该区间中众数的出现次数是 \(b\),初始时有 \(a>b\),而当区间扩展到全局时又有 \(a<b\),所以中间必存在一个状态满足 \(a=b\),此时这个解比刚刚的解更优,矛盾。
有了这个性质我们不难想到去枚举另一个众数 \(x\),我们现在需要找到一个区间满足全局众数和 \(x\) 出现次数相等。不难想到给全局众数的位置上赋值 \(1\),\(x\) 位置上赋值 \(-1\),则根据前缀和即可判断出最长的区间。不必担心这个区间中有出现次数更多的数,因为这样一定是更优解。这样做的复杂度是 \(O(nV)\) 的,可以通过简单版。
考虑到在本题中有一个关系:数字的出现次数 \(\times\) 出现次数为该次数的数字个数 \(\le n\),所以考虑根号分治。对于出现次数 \(>\sqrt n\) 的数字,个数不超过 \(\sqrt n\),此时利用刚才的方法做,复杂度 \(O(n\sqrt n)\);而对于出现次数 \(\le \sqrt n\) 的数字,考虑枚举出现次数,开桶用双指针维护合法区间即可。复杂度依然是 \(O(n\sqrt n)\) 的。
综上我们可以在 \(O(n\sqrt n)\) 的复杂度内解决这个问题。
H CF765F Souvenirs
考虑离线扫描线,我们对于每一个 \(i\) 维护 \([i,r]\) 中的答案,考虑如何维护 \(r-1\to r\) 时的答案变化。我们先考虑 \(a_x<a_r\) 的情况, 另一种情况是对称的。
容易想到的是我们维护一个单调递减的单调栈,这样的话我们对 \([1,x]\) 中的答案和 \(a_r-a_x\) 取 \(\min\) 即可。暴力枚举的话显然不可行,我们需要考虑减少枚举量。不难发现的是,假如我们有 \(x<y\),则此时必然有 \(a_x>a_y\),此时 \([1,x]\) 中已经和 \(a_x-a_y\) 取过 \(\min\) 了。那么如果 \(a_r-a_x\) 对答案有贡献,必然满足 \(a_r-a_x<a_x-a_y\)。移项后有 \(a_x>\tfrac{1}{2}(a_r+a_y)\)。
简单观察一下可以发现,每一次跳跃后 \(a_x\) 的范围都会除以 \(2\),所以最多跳跃 \(O(\log V)\) 次后就终止了。实现的时候需要找出 \(x\) 左边第一个值在某个范围内的数的位置,以及前缀 \(\text{chkmin}\)。用主席树和树状数组实现一下即可。单次操作复杂度 \(O(\log n\log V)\),总复杂度 \(O(n\log n\log V)\),可以通过。
I GYM103371M Yet Another Range Query Problem
其实这道题和 [NOIP2022] 比赛 是一模一样的。考虑离线扫描线,然后做一次差分,在 \(s-1\) 处和 \(e\) 处分别求出区间 \([l,r]\) 的贡献然后累加到答案中,现在考虑维护这个贡献。
我们对于每一个 \(l\) 维护当前最大值 \(A\),当前最小值 \(B\),两者乘积 \(A\times B\)。然后考虑当 \(r-1\to r\) 时这个值怎样改变,不难想到利用单调栈然后区间覆盖即可。不过我们知道题目要求的贡献并不是当前的最值,而是所有最值的历史和,所以我们在这里还需要维护一个 \(A\times B\) 的历史和,记作 \(C\)。
在这里为了方便维护历史和,我们可以把区间覆盖修改一下,利用颜色端均摊改为若干次区间加,这样总次数还是 \(O(n)\) 的。然后考虑怎样维护历史和,我们考虑在线段树上维护如下向量:
然后区间加最大值的矩阵标记对应为:
区间加最小值的矩阵同理:
然后最后我们需要给前缀区间打上历史和标记,这个矩阵为:
直接维护矩阵复杂度有点高,容易发现这个矩阵的主对角线全部都是 \(1\),且只有 \(9\) 个位置的值在改变,所以暴力维护出这九个标记即可。复杂度 \(O(n\log n)\),可以通过。
J CF643G Choosing Ads
首先我们考虑怎样求 \(p> 50\) 的情况,这个时候我们只需要求出区间的绝对众数即可。求绝对众数有一个经典的解法是摩尔投票,过程是我们维护一个集合 \(S\),从中任选两个不相等的数删去,最后剩下来的一定是一种数字,这就是绝对众数。当然了,上面的做法只能保证绝对众数出现在 \(S\) 中,不能保证这个数就是绝对众数,因为可能根本没有。不过这道题正好允许输出错解,所以是可以的。
而在这道题中还有一个性质是左右区间的绝对众数一定有一个是整个区间的绝对众数,证明可以考虑反证。所以可以用线段树来维护删除的过程。具体的,我们把左右区间剩下来的数再拼成一个集合 \(S\) 做一遍摩尔投票即可。
然后考虑 \(p\) 任意的情况,我们令 \(k=\left\lfloor\dfrac{100}{p}\right\rfloor\),那么我们的答案最多只有 \(k\) 个。仿照上面摩尔投票的解法,我们依然采取删数的策略维护这些众数,具体的,我们每一次从集合 \(S\) 中删去 \(k+1\) 个互不相等的数,最后剩下的 \(k\) 种数就是答案。
所以我们在线段树上的每个节点维护 \(k\) 种数的值和个数,然后合并的时候 \(O(k^2)\) 做一次摩尔投票即可。复杂度 \(O(nk^2\log n)\)。由于 \(k\le 5\) 所以可以通过。
K CF1209G2 Into Blocks (hard version)
首先需要看出 G1 怎么做,我们对于每个数字求出它第一次和最后一次出现的位置,记作 \(l_x,r_x\),那么我们有若干区间 \([l_x,r_x]\)。很显然,对于相交区间构成的连通块,我们一定要把他们变成同一种数字,此时显然保留出现次数最多的数不改变最优。
现在考虑形式化上面的做法。首先不难发现,每一个连通块都会占据原序列的一段区间 \([L,R]\)。而找到这个区间比较困难,考虑在这里转化一下。我们把每个数的区间看成 \([l_x,r_x)\),对这个区间加一,那么所有操作结束后 \(R\) 的值一定都是 \(0\),所以此时根据 \(0\) 的位置我们就可以知道每一个连通块的区间。
接下来我们需要在每个连通块内求出众数,一个经典的技巧是我们将一个数的出现次数 \(cnt_x\) 放到 \(l_x\) 处,这样我们求出区间最大值就是答案。然后注意到我们在线段树的一个区间中不一定是完整的连通块,比如下图:
.....0.....0.....0.....
此时左右两边并没有合成一个连通块,所以我们需要额外维护这两个值 \(lm,rm\) 表示左右两边没有确定的最大值,然后维护一个 \(sum\) 表示中间已经确定的连通块的最大值之和。需要注意的是这里的分界点并不一定是 \(0\),因为我们会区间修改,所以我们应该以整个区间中的最小值为分界点。合并的时候按照左右区间最小值的大小关系合并即可。
由于 \(n\) 处一定是 \(0\),所以整个区间的最小值一定是 \(0\),所以此时算出 \(n-sum-lm-rm\) 就是答案。注意标记拼接时的细节即可,复杂度 \(O(n\log n)\)。
L CF1270H Number of Components
首先手玩样例发现连通块一定构成了一个区间,证明的话可以考虑反证。
如果 \(x\) 在区间 \([l,r]\) 中,\([l,x)\) 和 \((x,r]\) 是一个连通块,但 \(x\) 不在该连通块中。那么设 \([l,x)\) 中最小值为 \(mn\),\((x,r]\) 中最大值是 \(mx\),则 \(x\) 应该满足 \(x<mn\) 且 \(mx<x\)。但是由于左右区间在同一连通块,所以有 \(mn<mx\),于是 \(x<mn<mx<x\),即 \(x<x\),矛盾!
然后我们考虑计算连通块个数。我们考虑一个连通块的结尾 \(i\),容易发现它要满足 \(\min\limits_{j=1}^i a_j>\max\limits_{j=i+1}^n a_j\)。现在我们需要计算满足这个条件的 \(i\) 的个数。
发现此时我们只关注数字之间的相对大小关系,所以考虑枚举一个中间值,枚举 \(\max a_j=w\),然后将序列中 \(\le w\) 的标为 \(0\),\(>w\) 标为 \(1\)。则合法序列必形如 \(111\cdots1000\cdots0\),那么我们实际上只需要维护出对于每个 \(w\) 构成的序列中 \(0,1\) 交替的次数,只有当这个次数等于 \(1\) 时才认为满足条件(强制钦定 \(a_0=1,a_{n+1}=0\))。
如此考虑用线段树维护,下标为 \(i\) 表示 \(w=i\) 时 \(0,1\) 交替次数。那么显然当 \(w\in [\min(a_i,a_{i+1}),\max(a_i,a_{i+1}) )\) 时,\(0,1\) 交替数会增加 \(1\),在线段树上区间修改即可。最后我们要统计次数为 \(1\) 的 \(w\) 的个数,这个还是利用经典套路,求出最小值及最小值个数即可。复杂度 \(O(n\log n)\),可以通过。
M CF1558F Strange Sort
直接做原问题比较困难,考虑到这个问题是一个排序类问题,所以我们考虑转化为 \(01\) 序列求解。具体的,我们枚举一个阈值 \(k\),令 \(a_i=[a_i> k]\),然后我们要做的就是让 \(01\) 序列排好序。可以证明,答案就是对于每一个 \(k\),取排序次数最大值。证明可以用归纳法简单证明,这里不再赘述。
现在的问题是维护出对 \(01\) 序列排序的结果。令 \(b_i\) 表示让第 \(i\) 个 \(0\) 走到第 \(i\) 个位置所需的步数,分类讨论:
-
如果第 \(i\) 个 \(0\) 本来就在 \(i\) 位置上,\(b_i=0\)。
-
如果第 \(i\) 个 \(0\) 中途碰到第 \(i-1\) 个 \(0\):
由于第 \(i\) 个 \(0\) 必然不能超过第 \(i-1\) 个,所以它所需步数至少是 \(b_{i-1}+1\)。
-
如果第 \(i\) 个 \(0\) 中途没有碰到第 \(i-1\) 个 \(0\):
那么直接看第 \(i\) 个 \(0\) 之前有多少个 \(1\) 即可,设为 \(k_i\)。同时还需要根据下标的奇偶性来判断是否第一次就开始走。令第 \(i\) 个 \(0\) 的下标是 \(p_i\),那么所需步数是 \(k_i+(p_i\bmod 2)\)。
综上有 \(b_i=\max(b_{i-1}+1,k_i+(p_i\bmod 2))\)。我们要求的是 \(b_i\) 最大值,显然最后一个 \(0\) 对应的 \(b\) 才是最大的。所以我们只用考虑最后一个 \(b\) 的值即可。令这个值为 \(b_m\)。
考虑枚举 \(m\) 前最后一个 \(b\) 取值为 \(k_i+(p_i\bmod 2)\) 的位置 \(i\),则有:
当然 \(i\) 不能取到 \(b_i=0\) 的部分,不然会假。我们考虑在每一个位置上维护这个值,令 \(c_i\) 表示 \(i\) 及以前 \(0\) 的个数,那么令 \(t_i=[a_i=0](i-c_i+(i\bmod 2)-c_i)=[a_i=0](i-2c_i+(i\bmod 2))\),我们求出 \(t_i\) 的最大值后加 \(m\) 就是答案。考虑如何维护 \(t_i\),显然随着 \(k\) 增加,我们每一次只会修改一个位置上的值变成 \(0\),那么我们就会给一段区间的 \(t\) 减 \(2\)。所以用线段树维护 \(t\) 的最大值即可,复杂度 \(O(n\log n)\)。
N CF1034D Intervals of Intervals
下文中大写字母 \([L,R]\) 表示序列上的区间,小写字母 \([l,r]\) 表示数轴上的区间。
首先考虑如何求出一段区间 \([L,R]\) 的价值,我们发现这个贡献难以拆分和合并,考虑利用前缀线性基的思路,离线下来然后扫描线,对于数轴上的每个位置维护最后一个覆盖它的区间,那么此时查询 \([L,R]\) 的价值就是所有编号在这个范围内的区间覆盖的点的个数。
也就是说当我们插入一个线段 \([l,r]\) 时,我们要将这个线段覆盖的所有部分的贡献删掉。显然这个可以用珂朵莉树直接维护出来,然后操作相当于前缀修改、单点查询,显然用树状数组维护即可。根据颜色端均摊理论,这样做的复杂度是 \(O(n\log n)\) 的。
然后考虑原题,我们要求出前 \(k\) 大区间的权值和,考虑先求出第 \(k\) 大区间的权值,然后把所有比它大的加起来。求第 \(k\) 大直接考虑二分,然后我们要数出所有权值大于等于当前 \(mid\) 的区间数量。直接按照上面的做法做的话复杂度是 \(O(n\log^2 n)\) 的(二分一个老哥,树状数组和珂朵莉一个老哥),不是很优秀,考虑优化。
先把珂朵莉的 \(\log\) 去掉,显然每一次我们修改操作都是一样的,所以可以只在外面做一次珂朵莉,用 vector 记录下每次修改的位置和值即可。然后去掉二分的 \(\log\),这里需要观察出一个性质:如果我们令 \(f(R)\) 表示最后一个使得 \([L,R]\) 权值大于等于 \(mid\) 的 \(L\),那么随着 \(R\) 增加,\(f(R)\) 单调不减。于是我们可以直接用双指针来完成二分的操作。
最后把树状数组的 \(\log\) 去掉,上文说过我们的操作只有前缀修改、单点查询,考虑差分后变为单点修改、前缀查询。然后因为每一次我们只统计 \(L\) 处的前缀和,同时 \(L\) 又是单调不降的,所以直接维护前缀和就可以知道当前区间的权值了。
最后要求出比第 \(k\) 大区间大的区间权值和,这个过程和上面是差不多的,维护前缀和以及前缀和的前缀和即可。当然要注意与第 \(k\) 大权值一样的时候不是全部取到,总共只能取 \(k\) 个。
那么我们的复杂度就可以降到 \(O(n\log n+n\log V)\),可以通过了。
O UOJ671【UNR #5】诡异操作
发现两个操作是区间除和区间与,显然两个操作都没有办法打懒标记实现。不过显然这两个操作对于一个数都最多做 \(\log V\) 次,也就是只减不增,考虑势能线段树。
区间除法是没有什么其他方法维护的,维护出整个区间是否非零,然后判断之后递归到叶子节点处修改单点值,复杂度 \(O(n\log V)\)。然后区间与有两种维护方式:一种是维护出区间或的值,判断之后递归到叶子节点处修改单点值;另一种是对于每一个二进制位维护有多少个数在这一位上有值,然后与操作就相当于对一位上区间赋值为 \(0\),打懒标记即可。
对于本题,由于两种操作并存,所以如果都要递归到叶子节点判断的话复杂度不是很能保证。所以考虑使用第二种方法维护区间与。这样的话我们所有操作都会额外带上一个 \(\log V\),总复杂度 \(O(n\log^2 V+q\log n\log V)\)。由于 \(\log V=128\) 所以过不去。
考虑复杂度瓶颈在于 \(\log V\),考虑能不能优化。容易发现的一点是,对于每一位最多有 \(O(n)\) 个数有值。那么如果我们将每一个二进制位对应的值写成二进制形式,则我们会有一个 \(\log V\times \log n\) 的只有 \(01\) 的表。而 \(\log n\) 是比 \(\log V\) 要小的,所以考虑将表格翻转,改为维护 \(\log n\times \log V\) 的表。
然后研究一下这个表的维护。打标记的时候相当于给一些列清零,也就是直接给每一行与一个值;上传标记的时候手动模拟一下二进制加法,用异或和与计算不进位加法和进位值;求区间和就是直接求出 \(\sum v_i\times 2^i\);单点除法直接对 \(v_0\) 操作即可。
此时我们所有操作都可以在 \(O(\log n)\) 的复杂度内完成,所以区间与和查询的复杂度就是 \(O(q\log^2n)\) 的。唯一不太明显的是除法的复杂度,对每个节点单独分析,一个节点最多会经过 \(\log V\) 次,每一次经过会上传和下放标记,而这个复杂度精细实现是 \(O(\log len)\) 的。所以这一部分的总复杂度是 \(O(\log V\sum \log len)\)。令 \(T(n)=O(\sum \log len)\),那么有 \(T(n)=2T(\tfrac{n}2)+O(\log n)\),由主定理知复杂度是 \(O(n)\) 的。所以这一部分的复杂度是 \(O(n\log V)\)。
综上我们的复杂度是 \(O(n\log V+q\log^2 n)\),简单卡一下常数就可以通过了。
P UOJ515【UR #19】前进四
考虑建立 \(x-t\) 坐标系,一般情况下是对 \(t\) 时间这一维扫描线,考虑反过来,对 \(x\) 下标这一维从后往前扫描线。然后维护一个以时间轴为下标的线段树,在每一个位置处的修改相当于对时间上的若干区间修改,不难发现这个区间数是 \(O(n)\) 的。
然后发现每一次修改相当于取最小值,然后将变化的地方加一。这个显然可以用吉司机线段树维护出来,复杂度 \(O(n\log n)\),可以通过。
Q UOJ681【UR #22】月球列车
看到题目中有二进制操作,不难想到拆位处理。那么我们对每一位考虑最后答案上这一位上的值是 \(0\) 还是 \(1\)。如果没有进位,这个问题是比较简单的,根据异或直接计算即可。但是现在的问题就是如何处理进位,在 \(\text{P}\) 题中我们运用的是与运算处理当前进位值,当然在此题中显然不可行。我们考虑进位的本质,如果存在 \(j\to j+1\) 的进位,则说明 \(a_i\) 的后 \(j\) 位加上 \(v\) 的后 \(j\) 位值会 \(\ge 2^{j+1}\)。那么如果我们按照 \(a_i\) 的后 \(j\) 位排序,则有进位的部分是一段后缀,那么这个就比较好求了。
此时的总复杂度是 \(O(n\log n\log V)\) 的,无法通过。考虑复杂度瓶颈在于排序和二分的 \(\log n\),我们尝试将其优化掉。排序的 \(\log\) 是容易搞掉的,每次归并排序一下即可。难点在于优化掉二分的 \(\log\)。
容易发现我们二分实际上关注的是当前位上有多少个数不进位,那么这个实际上是可以根据上一位的结果直接算出来的。具体的,我们可以分类讨论一下:
-
当 \(v\) 的第 \(j\) 位是 \(1\):
此时只有一种情况没有进位:前 \(j-1\) 位没有进位且 \(a_i\) 的第 \(j\) 位为 \(0\)。
-
当 \(v\) 的第 \(j\) 位为 \(0\):
此时也只有一种情况没有进位:前 \(j-1\) 位没有进位,\(a_i\) 的第 \(j\) 位是 \(0\) 还是 \(1\) 都没有关系。
那么我们对于每一个二进制位维护一个前缀和,存下一位上 \(0\) 和 \(1\) 的出现个数。那么根据上面得到的结论就可以直接计算出当前不进位的数的个数,然后二分的 \(\log\) 就去掉了。复杂度 \(O(n\log V)\),可以通过。

浙公网安备 33010602011771号