List
Easy Problem
CF840D\color {blue} {CF840D}CF840D
CF1422D\color {green} {CF1422D}CF1422D
P2048\color {blue} {P2048}P2048
P5283\color {purple} {P5283}P5283
Good Problem
P2303\color {blue} {P2303}P2303
P4768\color {blue} {P4768}P4768
CF1416D\color {blue} {CF1416D}CF1416D
P5025\color {purple} {P5025}P5025
Tips
CF804D
考虑主席树。
每次询问执行主席树上扫描即可。假设本题询问的区间是[L,R][L,R][L,R],当前扫到的值域区间[l,r][l,r][l,r]。如果左子区间的数在[L,R][L,R][L,R]中出现的次数之和超过了R−L+1k\frac {R-L+1} kkR−L+1,那么搜索左区间,否则搜索右区间;如果都没超过直接返回。
考虑这么做的时间复杂度是多少。不难发现,对于主席树上的每一层至多只有kkk个位置被扫描到并继续向下进行搜索;所以总时间复杂度是O(nklogn)O(nk \log n)O(nklogn)的。
P2303
通过欧拉反演,不难得到答案为sumd∣nφ(i)nisum_{d|n} \varphi(i) {\frac n i}sumd∣nφ(i)in。
直接枚举因数是O(n)O(\sqrt n)O(n)的,但是单次计算欧拉函数的代价之和很高。并且这个式子不能使用杜教筛来优化。
参考一下根号分治的思想我们不难得到正解。我们通过线性筛求出所有不超过limlimlim的数的φ\varphiφ值。我们找到所有nnn的约数pip_ipi,如果pip_ipi的值不超过limlimlim直接调用,否则暴力在根号复杂度内计算φ\varphiφ值。
当limlimlim取到n0.72n^{0.72}n0.72的时候可以通过。
CF1422D
考虑规划一条路径为“从一个闪现点到另一个闪现点”,不难发现两点(a,b)(c,d)(a,b)(c,d)(a,b)(c,d)之间的路径距离为min(∣a−c∣,∣b−d∣)min(|a-c|,|b-d|)min(∣a−c∣,∣b−d∣)。
但是,如果我们枚举所有的点对并连边的话时间复杂度,并跑最短路的时间复杂度为O(n2)O(n^2)O(n2)。我们需要优化这个算法。
首先,我们对模型进行一步转化。两点(a,b)(c,d)(a,b)(c,d)(a,b)(c,d)之间我们可以连两条边,边权分别是∣a−c∣|a-c|∣a−c∣和∣b−d∣|b-d|∣b−d∣,这样就将两点之间的边权式子给简化了。同时,我们发现,对于满足横坐标分别为x1,x2,x3x_1,x_2,x_3x1,x2,x3的1,2,31,2,31,2,3号点,111到222的距离为x2−x1x_2-x_1x2−x1,222到333的距离为x3−x2x_3-x_2x3−x2,而111到333的距离是111到222的距离与222到333的距离之和!所以,我们根本不用连接111与333号点。扩展到多个点的情况,我们只需要将横坐标排序并将相邻两个点做连边操作,再对纵坐标排序并将相邻两个点做连边操作即可。
由于边数为2(n−1)2(n-1)2(n−1),点数为nnn,所以跑最短路的复杂度是O(nlogn)O(n \log n)O(nlogn),可以通过。
P4786
NOI 2018 D1T1竟然是个裸题,NOI 2020 D1T1也是
首先,可爱的Yazid一定是开车到达某个节点rtrtrt并下车之后步行到终点。
我们建出关于海拔的Kruskal重构树。注意建立的应该是最大生成树而非最小生成树。根据此时重构树的小根堆性质,此时两个点能够互相到达当且仅当它们的LCA的点权大于ppp。
此时我们先考虑一个暴力做法: 在Kruskal重构树上枚举这个LCA,要求这个LCA的点权必须大于ppp;对于所有满足要求的LCA,与vvv异子树的叶节点的disdisdis值的最小值即是答案。这里一个节点的disdisdis表示它与111号节点的最短路径长度。
考虑如何优化这个暴力。首先,disdisdis要用Dijkstra预处理出来①^{①}①。然后,我们处理出每一个子树内的所有叶节点的disdisdis的最小值。对于每次询问,我们直接倍增即可求出答案。
时间复杂度为O(T×nlogn)O(T×n \log n)O(T×nlogn)。
①: 这句话是有深意的。这道题是OI历史上的一个里程碑——SPFA的死去。特加这一个注释,悼念已经离我们而去的SPFA算法。事实上,SPFA并没有完全死去,在Johnsen全源最短路、差分约束、负环判定以及一些极其难卡掉(如一些神奇的费用流建图题)或者数据完全随机的题目中有复杂度为线性,有重要的作用。
CF1416D
考虑给每条边一个边权,表示它在什么时候被删去。特别的,对于那些一直没有被删去的边,令它的权值为q+1q+1q+1。
然后我们正序扫描一遍所有询问。不难发现第iii次询问的答案为: 从uuu开始只经过权值不超过iii的边所能到达的节点的最大点权,并将这个点的点权赋为000。
是不是很熟悉这个模型?我们可以建立出一个关于边权的Kruskal重构树。每次查询的符合要求的节点均在一个子树中,这个子树的根可以倍增找到;找到之后,我们需要查询最小值并动态修改,可以采用dfs序+线段树来维护。
时间复杂度O((n+m)logn)O((n+m) \log n)O((n+m)logn),比LCT不知道可爱到哪里去了。
P5025
套路题。
首先考虑暴力做法。先O(n2)O(n^2)O(n2)建图,然后缩点并执行一次拓扑排序(dpdpdp)。
考虑如何优化。不难发现这个连边有一个性质: 每个节点连向的点都在一个区间中。所以可以采用线段树来优化这个建图。线段树优化的建图同样可以缩点,只不过每个点本身都有一个权值。
我们可以通过二分找到每次连边的区间[l,r][l,r][l,r]。时间复杂度为O(nlogn)O(n \log n)O(nlogn)。
P2048
大套路题,然而我不会。
令prei=∑j=1iajpre_i=\sum_{j=1}^i a_jprei=∑j=1iaj,那么不难得到∑i=lrai=prer−prel−1\sum_{i=l}^r a_i=pre_r-pre_{l-1}∑i=lrai=prer−prel−1。于是我们将题目做一个转化——在序列中选kkk对差在lll到rrr之间的数,可以多次重复选择同一个数,使得每一对数的后者的preprepre减去前者的preprepre之和最大。
我们维护许多五元组(l,x,y,pos,val)(l,x,y,pos,val)(l,x,y,pos,val),表示选取的第一个位置lll,第二个位置在[x,y][x,y][x,y]区间中;满足prepos−prelpre_{pos}-pre_{l}prepos−prel最大的位置为pospospos且这个最大值为valvalval。
我们将这些五元组扔进一个大根堆中。每次我们取出堆顶,并将这个区间拆分为两个形如(l,x,pos−1,pos′,val′)(l,x,pos-1,pos',val')(l,x,pos−1,pos′,val′)的子五元组,分别重新计算这两个子区间的pos′pos'pos′与val′val'val′。
采用RMQ来快速计算即可,时间复杂度为O(nlogn+klogn)O(n \log n+k \log n)O(nlogn+klogn)。
异或粽子这题只需要将RMQ求区间最大值改为可持久化Trie树上查询,时间复杂度为O(nlogm+klognlogm)O(n \log m+k \log n \log m)O(nlogm+klognlogm),其中mmm为所有aia_iai的最大值。
本文精选多道算法竞赛题目,涵盖主席树、欧拉反演、最短路径等高级算法,解析高效解题思路,包括优化算法的时间复杂度,如采用线段树优化建图过程等。
171

被折叠的 条评论
为什么被折叠?



