给定 n n n 个点,每个点向另一个点连一条有向边,求一个最大的点集使得点集内部点间没有连边。
n ≤ 5 ∗ 1 0 5 n \leq 5 * 10^5 n≤5∗105
就是 [ZJOI2008] 骑士。
基环内向树森林上的dp。
枚举断边后dp可以做;找到环后对每棵环上树分别dp,再在环上dp一遍也可以做。
复杂度 O ( n ) O(n) O(n)。
给定一个长 m m m 的数列(初始全为 0), n n n 次操作,每次操作选择一个位置 + 1 +1 +1 后使总代价加上那个位置的值。你有 k k k 次机会随时将一个位置清零。最小化总代价。
n ≤ 1 0 6 n \leq 10^6 n≤106, m ≤ 100 m \leq 100 m≤100, k ≤ 500 k \leq 500 k≤500
容易发现可以对每个位置分别考虑。
如果确定给某位置分配多少次清零,可以很快地算出最优代价。
这样就变成了一个 m m m 个组的分组背包,正常dp即可。
复杂度 O ( n + m k 2 ) O(n + mk^2) O(n+mk2)。
给定一棵有边权, n n n 个点的树,以及 m m m 个目的地(在树上,不重复)。
对于每个点,你要确定以当前点作为起点的一条连续,可重复经过边的路径,使得遍历完所有目的地且边权和最小。(通过同一条边多次则计算多次)
m ≤ n ≤ 5 ∗ 1 0 5 m \leq n \leq 5 * 10^5 m≤n≤5∗105
换根树形dp。
对于每个点计算出从当前点到它子树中所有目标点且 回来/不回来 的最小边权和。用一遍树形dp可解。
然后跑一遍换根dp就完事了。
细节很恶心。总复杂度 O ( n ) O(n) O(n)。
给定一个 n ∗ m n * m n∗m 的矩阵,求值全相等的子矩阵个数。
n , m ≤ 1000 n,m \leq 1000 n,m≤1000, V ≤ 1 0 9 V \leq 10^9 V≤109
预处理出每个位置向上的最大长度。
对于每行单调栈扫一遍统计答案。
复杂度 O ( n 2 ) O(n^2) O(n2)。
给定一个 n ∗ n n * n n∗n 的矩阵,每个位置有一个初始值和一个增长速率。问在所有时刻中,值全相等的最大连通块大小最大是多少?
n ≤ 700 n\leq 700 n≤700, V ≤ 1 0 6 V \leq 10^6 V≤106
使用可撤销并查集实现。只考虑相邻两位置何时相等。
先将恒相等关系合并。
然后枚举可能产生相等的时刻,对于每个时刻将所有相等关系合并,统计答案,然后撤销。
总复杂度 O ( n 2 log n ) O(n^2\log n) O(n2logn)。
给定一个长为 n n n 的序列,一个连续子序列的权值定义为:
子序列最大值 * 子序列最小值 * 子序列长度
求所有连续子序列权值和。答案对 1 0 9 10^9 109 取模。
n ≤ 5 ∗ 1 0 5 n \leq 5 * 10^5 n≤5∗105
考虑分治处理。
对于 [ l , r ] [l,r] [l,r] 内所有子序列的贡献,可以拆为:
[ l , r ] [l,r] [l,r] 内经过 m i d mid mid 与 m i d + 1 mid+1 mid+1 的子序列的贡献和 + [ l , m i d ] [l,mid] [l,mid] 内所有子序列的贡献和 + [ m i d + 1 , r ] [mid+1,r] [mid+1,r] 内所有子序列的贡献和。
这样,为了分治处理只需考虑如何计算过定点的所有子序列的贡献和。
我们作出如下定义:
l m a x i lmax_i lmaxi: [ i , m i d ] [i,mid] [i,mid] 中的最大值
l m i n i lmin_i lmini: [ i , m i d ] [i,mid] [i,mid] 中的最小值
r m a x i rmax_i rmaxi: [ m i d + 1 , i ] [mid+1,i] [mid+1,i] 中的最大值
r m i n i rmin_i rmini: [ m i d + 1 , i ] [mid+1,i] [mid+1,i] 中的最小值
m x s u m i mxsum_i mxsumi: ∑ j = m i d + 1 i r m a x j \sum_{j=mid+1}^{i} rmax_j ∑j=mid+1irmaxj
m n s u m i mnsum_i mnsumi: ∑ j = m i d + 1 i r m i n j \sum_{j=mid+1}^{i} rmin_j ∑j=mid+1irminj
m 2 s u m i m2sum_i m2sumi: ∑ j = m i d + 1 i r m a x j ∗ r m i n j \sum_{j=mid+1}^{i} rmax_j * rmin_j ∑j=mid+1irmaxj∗rminj
m x L s u m i mxLsum_i mxLsumi: ∑ j = m i d + 1 i j ∗ r m a x j \sum_{j=mid+1}^{i} j * rmax_j ∑j=mid+1ij∗rmaxj
m n L s u m i mnLsum_i mnLsumi: ∑ j = m i d + 1 i j ∗ r m i n j \sum_{j=mid+1}^{i} j * rmin_j ∑j=mid+1ij∗rminj
m 2 L s u m i m2Lsum_i m2Lsumi: ∑ j = m i d + 1 i j ∗ r m a x j ∗ r m i n j \sum_{j=mid+1}^{i} j * rmax_j * rmin_j ∑j=mid+1ij∗rmaxj∗rminj
每次处理一段区间 [ l , r ] [l,r] [l,r] 时,一遍扫计算如上 10 个数组。
那么在计算贡献时,从 l l l 到 m i d mid mid 枚举左端点,得到 l m i n i lmin_i lmini 与 l m a x i lmax_i lmaxi ,并同时开单调队列找到 [ m i d + 1 , r ] [mid+1,r] [mid+1,r] 前缀最值数组中从左到右第一个 r m i n < l m i n i rmin < lmin_i rmin<lmini 与第一个 r m a x > l m a x i rmax > lmax_i rmax>lmaxi 的位置,记作 m i n l i m minlim minlim 与 m a x l i m maxlim maxlim。
这样的话,我们可以分类讨论右端点的位置来得出贡献。此处令左端点为 i i i。
那么可以直接调用 l m i n i lmin_i lmini 和 l m a x i lmax_i lmaxi 计算贡献。
可以用如下公式描述这些子段的总贡献:
a n s = ∑ j = m i d + 1 min ( m i n l i m , m a x l i m ) − 1 ( j − i + 1 ) ∗ l m i n i ∗ l m a x i ans = \sum_{j=mid+1}^{\min(minlim,maxlim)-1} (j-i+1) * lmin_i * lmax_i ans=j=mid+1∑min(minlim,maxlim)−1(j−i+1)∗lmini∗lmaxi
提出后,用等差数列公式可以化简。
若 m i n l i m ≤ m a x l i m minlim \leq maxlim minlim≤maxlim,那么这段的最小值在变化,而最大值可以调用 l m a x i lmax_i lmaxi。
a n s = ∑ j = m i n l i m m a x l i m − 1 ( j − i + 1 ) ∗ r m i n j ∗ l m a x i ans = \sum_{j=minlim}^{maxlim-1} (j-i+1) * rmin_j * lmax_i ans=j=minlim∑maxlim−1(j−i+1)∗rminj∗lmaxi
提出化简为已知得
a n s = l m a x i ( m n L s u m m a x l i m − 1 − m n L s u m m i n l i m − 1 − ( i − 1 ) ( m n s u m m a x l i m − 1 − m n s u m m i n l i m − 1 ) ) ans = lmax_i (mnLsum_{maxlim-1}-mnLsum_{minlim-1}-(i-1)(mnsum_{maxlim-1}-mnsum_{minlim-1})) ans=lmaxi(mnLsummaxlim−1−mnLsumminlim−1−(i−1)(mnsummaxlim−1−mnsumminlim−1))
反之,则这段的最大值在变化,而最小值可以调用 l m i n i lmin_i lmini。
a n s = ∑ j = m a x l i m m i n l i m − 1 ( j − i + 1 ) ∗ l m i n i ∗ r m a x j ans = \sum_{j=maxlim}^{minlim-1} (j-i+1) * lmin_i * rmax_j ans=j=maxlim∑minlim−1(j−i+1)∗lmini∗rmaxj
提出化简为已知得
a n s = l m i n i ( m x L s u m m i n l i m − 1 − m x L s u m m a x l i m − 1 − ( i − 1 ) ( m x s u m m i n l i m − 1 − m x s u m m a x l i m − 1 ) ) ans = lmin_i (mxLsum_{minlim-1}-mxLsum_{maxlim-1}-(i-1)(mxsum_{minlim-1}-mxsum_{maxlim-1})) ans=lmini(mxLsumminlim−1−mxLsummaxlim−1−(i−1)(mxsumminlim−1−mxsummaxlim−1))
这段的最大值与最小值和 l m a x i lmax_i lmaxi 与 l m i n i lmin_i lmini 没有关系。
a n s = ∑ j = max ( m i n l i m , m a x l i m ) r ( j − i + 1 ) ∗ r m i n j ∗ r m a x j ans = \sum_{j=\max(minlim,maxlim)}^{r} (j-i+1) * rmin_j * rmax_j ans=j=max(minlim,maxlim)∑r(j−i+1)∗rminj∗rmaxj
简单化简得
a n s = m 2 L s u m r − m 2 L s u m max ( m i n l i m , m a x l i m ) − 1 − ( i − 1 ) ( m 2 s u m r − m 2 s u m max ( m i n l i m , m a x l i m ) − 1 ) ans = m2Lsum_{r}-m2Lsum_{\max(minlim,maxlim)-1}-(i-1)(m2sum_{r}-m2sum_{\max(minlim,maxlim)-1}) ans=m2Lsumr−m2Lsummax(minlim,maxlim)−1−(i−1)(m2sumr−m2summax(minlim,maxlim)−1)
这样我们就能对于每个左端点 O ( 1 ) O(1) O(1) 算出答案。
分治总复杂度: O ( n log n ) O(n\log n) O(nlogn)。
给定 n n n 个人,每个人有三个得分 x x x, y y y, z z z,但是每个人的 z z z 并不确定,只有 x x x, y y y 已知。如果一个人的 x x x 与 y y y 均严格大于另一个人的 x x x 与 y y y(即成二维偏序),则我们可以认为这个人的 z z z 大于等于那个人的 z z z。现在请确定在满足如上假设的最极限情况下,每个人最高与最低的 x + y + z x+y+z x+y+z 值的排名。
n ≤ 5 ∗ 1 0 5 n \leq 5 * 10^5 n≤5∗105, 0 ≤ x , y , z ≤ 650 0 \leq x,y,z \leq 650 0≤x,y,z≤650
如果一个人 i i i 的 x , y x,y x,y 均大于另一个人 j j j,我们称 i i i 限制 j j j。
首先考虑最高排名,对于每个人 i i i 可以假设除限制其的所有人以外 z = 0 z=0 z=0,而 i i i 及限制 i i i 的人的 z = 650 z=650 z=650。
那么我们具体需要知道:
稍加思索,发现 2 类包含 1 类。所以我们只需要知道 2 类的人数。这是经典二维偏序问题。
然后考虑最低排名,对于每个人 i i i 可以假设除 i i i 限制的所有人以外 z = 650 z=650 z=650,而 i i i 及 i i i 限制的人的 z = 0 z=0 z=0。
我们反着考虑,考虑一定排在 i i i 之后的人有多少个。
那么我们具体需要知道:
这样就类似最高排名的情况了,有 2 类包含 1 类。有一些不同的地方是,当 x , y x,y x,y 至少有一个为 650 时要特判。
同样是经典二维偏序问题。总复杂度 O ( n log n ) O(n\log n) O(nlogn)。
启示: 对于最低排名,如果正着考虑,就只能按总和排序后使用二维树状数组维护,时间变成 O ( n log 2 n ) O (n\log^2 n) O(nlog2n),无法通过。所以,科学地反着考虑问题有时能很好地起到优化复杂度的作用。
再启示: 不必要纠结用树状数组维护偏序。考虑值域只有 650,可以提前使用二维前缀和直接预处理出来所有正向与反向的偏序关系。复杂度 O ( V 2 + n ) O(V^2 + n) O(V2+n)。要充分利用题目的性质!!!
初始给定一个空栈,编号为 1 1 1。共 q q q 次操作,第 i i i 次编号为 i i i。每次操作前,先选定编号为 x x x 的栈并将其复制,新栈编号为操作编号;然后对新栈进行如下操作的一种:
你要实现一种数据结构,实现如上的操作。
q ≤ 3 ∗ 1 0 5 q \leq 3 * 10^5 q≤3∗105
可以从构建树形结构的方向上考虑。
每个栈视为一个点,存一个值记录栈顶元素。同时用并查集处理栈间相等关系。
对于 1 操作,从原栈向新栈连边,使原栈成为新栈父亲。
对于 2 操作,输出原栈处所存的值。新栈必定和原栈的父亲相同,将两者合并。
对于 3 操作,即为求两栈 LCA 深度。新栈必然和第一原栈相同,将两者合并。
每次在调用“原栈”时,应该调用其在并查集上的根。
离线预建出整棵树是可行的,在线的话就用 LCT 大力维护 LCA 或者连到每个点的时候处理一遍倍增数组。
复杂度 O ( q log q ) O(q\log q) O(qlogq)。
数个城市依次排列位于一个数轴上,编号为 i i i 的城市位于数轴坐标为 i i i 处,可见编号相邻的两城市之间距离为 1 1 1。
n n n 辆卡车,每辆卡车有一个长度为 k k k 的路径,描述它依次要经过的城市编号。这个路径是波动的,即,令 A i A_i Ai 为路径第 i i i 项,则:
A 1 < A 2 > A 3 < A 4 > ⋯ 或 A 1 > A 2 < A 3 > A 4 < ⋯ A_1 < A_2 > A_3 < A_4 > \cdots \ \text{或} \ A_1 > A_2 < A_3 > A_4 < \cdots A1<A2>A3<A4>⋯ 或 A1>A2<A3>A4<⋯
显然路径上每个点都可被称作一个“拐点”。
所有卡车同时出发,起始点为其路径第 1 1 1 个点,速率都为 1 1 1。当一辆卡车到达它路径的终点的瞬间,它就会消失。当两辆卡车同时位于某一处(不一定必须是某个城市)时,我们称它们相遇。
m m m 次询问,每次选择两辆卡车 x , y x,y x,y,问它们相遇的次数。
读入保证询问的两辆卡车不会在其中任意一辆卡车达到路径上某一拐点时相遇。
n , m ≤ 1 0 5 n,m \leq 10^5 n,m≤105, ∑ k ≤ 3 ∗ 1 0 5 \sum k \leq 3 * 10^5 ∑k≤3∗105, A i ≤ 1 0 9 A_i \leq 10^9 Ai≤109
首先把所有卡车的所有拐点合在一起,按达到的时间排序。
把所有询问离线下来。对于每个询问,找到两卡车中拐点较少者,绑定在该车上。对于重复的询问,直接记忆化判掉。
可以发现,任意一车的两拐点之间两车最多相遇一次,并且在这道题目中不存在追及。
在被绑定车的两拐点之间,我们画出原询问的两辆车的 x-t 图。
容易发现,被绑定车的图像一定是一条单调直线,而另一车的图像是分段单调的折线。(图来源:Mr_Wu 手绘)
那么如果这两辆车有交,就只能是两辆车的相对左右关系在两拐点处不同。
又考虑,每个询问只会考虑被绑定车的所有拐点,所以我们在得出当前这个拐点处询问的两车的左右关系时,可以将这个关系连带存在询问上。这样,在到达绑定车的下一个拐点时,就容易判断了。
那么,枚举所有拐点,动态设定一辆车的位置和方向。每枚举到一个拐点,枚举和拐点所属车绑定的所有询问,处理即可。
另外,对于在到达某一拐点前除绑定车以外的另一辆询问车消失的情况,也是有可能相遇的。处理比较恶心,需要特判。(并且样例里面没有这个情况,如果你样例全过却 wa 声一片,可以看看是不是这个原因)
时间复杂度分析:
考虑可能发生的所有询问以及其对应绑定的卡车路径长度。从大到小排序后前 q q q 项和就是最大枚举复杂度。
我们令每辆卡车的路径长度从大到小为 k 1 , k 2 , ⋯ , k n k_1,k_2,\cdots,k_n k1,k2,⋯,kn。
那么最大的枚举复杂度显然为 1 ∗ k 2 + 2 ∗ k 3 + ⋯ 1 * k_2 + 2 * k_3 + \cdots 1∗k2+2∗k3+⋯(系数和为 q q q)
随意放缩简化一下,令上述式子没有包含的 k i = 0 k_i = 0 ki=0,并假定式子即为 ∑ i = 1 x i ∗ k i \sum_{i=1}^x i * k_i ∑i=1xi∗ki 的形式。
基于等差数列求和公式,可得 x x x 在 q \sqrt{q} q 左右。不妨令 x = q x=\sqrt{q} x=q。
那我们的问题转化为:
已知 ∑ i = 1 q k i = k \sum_{i=1}^{\sqrt{q}} k_i = k ∑i=1qki=k,求 max { ∑ i = 1 q i ∗ k i } \max \{ \sum_{i=1}^{\sqrt{q}} i * k_i \} max{∑i=1qi∗ki}。
根据大小关系,易得 k i ≤ k i k_i \leq \frac{k}{i} ki≤ik,因此时间复杂度 O ( k q ) O (k \sqrt{q}) O(kq)。
启示: 有时候,看上去毫无道理的暴力却有很优秀的复杂度,还是要多详细分析。
T1-MAFIJA(由于代码复杂度不正确,暂缓放出)
T2-ZABAVA
T3-KAMP
T4-BOB
T5-SUMA
T6-NORMA
T7-COCI
T8-STOGOVI
T9-KAMIONI