小结(记于 7.1)
共 19 题.
关键词索引:线段树标记,博弈论,李超线段树,树上括号序列(点对问题),分裂搜索,prufer序列,射线法.
CF1824D LuoTianyi and the Function
先来拆一下询问吧.
∑
i
=
l
r
∑
j
=
x
y
G
(
i
,
j
)
=
∑
i
=
l
r
∑
j
=
1
y
G
(
i
,
j
)
−
∑
i
=
l
r
∑
j
=
1
x
−
1
G
(
i
,
j
)
=
∑
i
=
l
min
(
r
,
y
)
∑
j
=
i
y
G
(
i
,
j
)
−
∑
i
=
l
min
(
r
,
x
−
1
)
∑
j
=
i
x
−
1
G
(
i
,
j
)
\begin{aligned} \sum_{i=l}^{r}\sum_{j=x}^{y}G(i, j) &=\sum_{i=l}^{r}\sum_{j=1}^{y}G(i, j)-\sum_{i=l}^{r}\sum_{j=1}^{x-1}G(i, j)\\ &=\sum_{i=l}^{\min(r,y)}\sum_{j=i}^{y}G(i, j)-\sum_{i=l}^{\min(r, x-1)}\sum_{j=i}^{x-1}G(i, j) \end{aligned}
i=l∑rj=x∑yG(i,j)=i=l∑rj=1∑yG(i,j)−i=l∑rj=1∑x−1G(i,j)=i=l∑min(r,y)j=i∑yG(i,j)−i=l∑min(r,x−1)j=i∑x−1G(i,j)
转化为处理形如 Q ( l , r , x ) = ∑ i = l r ∑ j = 1 x G ( i , j ) \displaystyle Q(l, r, x)=\sum_{i=l}^{r}\sum_{j=1}^{x}G(i, j) Q(l,r,x)=i=l∑rj=1∑xG(i,j) 的询问,发现这是一个寻常的区间子区间问题,考虑扫描线求解.
具体的,扫描 x x x.维护数组 g i = ∑ j = i x G ( i , j ) \displaystyle g_i=\sum_{j=i}^{x}G(i, j) gi=j=i∑xG(i,j).加入位置 x x x,对 g g g 造成变化 g i + G ( i , x ) → g i g_i+G(i, x) \rightarrow g_i gi+G(i,x)→gi.而对于询问有 Q ( l , r , x ) = ∑ i = l r g i \displaystyle Q(l, r, x)=\sum_{i=l}^{r}g_i Q(l,r,x)=i=l∑rgi.
可以用支持区间覆盖,区间历史版本求和的线段树维护.
然后新加进 x x x 位置时,要分析 G ( i , x − 1 ) G(i, x-1) G(i,x−1) 到 G ( i , x ) G(i, x) G(i,x) 的变动,这就是一个 dirty work 了.诶,真不想写了,到这来.
现在着重分析一下线段树在 历史版本和 之类特殊应用时出现其中元素相互作用的维护方法.目前只能总结一种比较简单的(当然也是可能用到的比较多的)情况.
线段树上有元素 a , b a, b a,b 和 c c c(线段长度),需要支持操作将 a a a 或 b b b 变成 a , b , c a,b,c a,b,c 的线性复合.
介绍两种方法
- 矩阵表达标记.
( a b c ) ∗ ( k a , a k b , a 0 k a , b k b , b 0 k a , c k b , c 1 ) = ( a ′ b ′ c ′ ) \begin{pmatrix} a & b & c \end{pmatrix} * \begin{pmatrix} k_{a,a} & k_{b,a} & 0\\ k_{a,b} & k_{b,b} & 0\\ k_{a,c} & k_{b,c} & 1\\ \end{pmatrix} = \begin{pmatrix} a^{\prime} & b^{\prime} & c^{\prime} \end{pmatrix} (abc)∗ ka,aka,bka,ckb,akb,bkb,c001 =(a′b′c′)
a ′ = k a , a a + k a , b b + k a , c c b ′ = k b , a a + k b , b b + k b , c c \begin{aligned} a^{\prime}&=k_{a, a}a+k_{a, b}b+k_{a, c}c \\ b^{\prime}&=k_{b, a}a+k_{b, b}b+k_{b, c}c \end{aligned} a′b′=ka,aa+ka,bb+ka,cc=kb,aa+kb,bb+kb,cc
不同操作,使用不同的矩阵表达出线性复合的式子即可.在线段树上维护 a , b , c a,b,c a,b,c 等元素构成的向量,并将矩阵作为修改标记,就可以毫无压力地进行修改查询和标记下传等一系列操作了.
- 手推标记.
对于一类特定的修改可以较为方便地手推标记.即在线性复合中仅存在一个元素在其复合式中存在其他元素,而其他元素的复合式中仅存在自己和常数.
我们现在拿上面讲到的题举例吧,有元素
s
,
a
s, a
s,a,需要支持对
a
a
a 区间覆盖,将
s
s
s 变成
s
+
a
s+a
s+a.
s
s
s 的复合中存在
a
a
a,而
a
a
a 的复合中没有
s
s
s.发现
a
a
a 的标记是平凡的,只需要研究
s
s
s 的标记就可以了.
维护三个标记 t g a , t g c , c v a tg_a,tg_c,cv_a tga,tgc,cva 分别表示对于 s s s 加上的 a a a 的倍数,对 s s s 加上的 c c c 的倍数,对 a a a 的覆盖.标记下传的顺序与介绍时罗列它们的顺序一致.这里的标记下传顺序既包括标记对子节点的 s , a s,a s,a 进行影响的顺序,同时也是标记复合到子节点标记上的顺序.
我们可以将标记储存在节点上状态想像成一个队列,头出(下传到子节点)尾进(父节点下传).发现标记队列中往往只有相邻标记才能合并(标记并不具有交换律).比如若在队列 t g a , t g c , c v a {tg_a,tg_c,cv_a} tga,tgc,cva 后插入 t g a ′ tg_a^{\prime} tga′,则不能和队列头部的 t g a tg_a tga 合并,因为 c v a cv_a cva 已经将 a a a 的信息改变了.为了维持标记队列的长度小于等于设计的标记个数,我们考虑将不能合并的标记拆掉分给队列中的其他标记.具体到这一题上,在存在 c v a cv_a cva 的时候, t g a ′ tg_a^{\prime} tga′ 复合到标记中可以表达成 t g c + c v a ∗ t g a ′ → t g c tg_c+cv_a*tg_a^{\prime} \rightarrow tg_c tgc+cva∗tga′→tgc.即对 a a a 数组进行覆盖后, s s s 的增量可以直接计算.
“在线性复合中仅存在一个元素在其复合式中存在其他元素,而其他元素的复合式中仅存在自己和常数”这个限制其实并不是必须的.但复合复杂的式子标记间的互相作用同样会很复杂,手推就不便于实现和排查错误了.而且随着转移式的稠密,手推标记对比矩阵带来的时间常数优势也在减弱.
在两种方法后的补充:
- 线性信息的转化.
矩阵表达和手推两种方法的相同点是要满足更改是元素的线性复合.但有一些题目会存在元素相乘的项,如 [NOIP2022] 比赛.它需要支持
s
+
a
b
→
s
s+ab \rightarrow s
s+ab→s 和
a
+
c
→
a
a+c \rightarrow a
a+c→a,
b
+
c
→
b
b+c \rightarrow b
b+c→b.若只维护
a
b
ab
ab,则对
a
a
a 或
b
b
b 实行区间加法后无法使用维护元素的线性复合表达出新的
a
b
ab
ab(即实现更新操作).我们考虑将乘积项中的
a
,
b
a,b
a,b 添上修改后将括号打开(成线性形式),从而快速找到需要维护的值.
(
a
+
c
1
)
(
b
+
c
2
)
=
a
b
+
c
2
a
+
c
1
b
+
c
1
c
2
(a+c_1)(b+c_2)=ab+c_2a+c_1b+c_1c_2
(a+c1)(b+c2)=ab+c2a+c1b+c1c2
这说明给定的某一修改操作, a b ab ab 需要用 a b , b , a , c ab,b,a,c ab,b,a,c 四个值才能把修改后的值表达出来.这样就转化为了修改为线性复合的问题了.
- 矩阵的优化方法.
这个就比较玄幻了,可以写一份代码随机累乘转移矩阵,会发现矩阵中存在一些地方一直为零,丢弃它们,将矩阵中有值的点单独拿出来维护,即可大大降低时间复杂度.
LOJ #6031 「雅礼集训 2017 Day1」字符串
记 q k = W qk=W qk=W.发现可以根号分治(这启示我们,题目中数据限制的乘积一定时可以向根号分治考虑).现将 s s s 串建出 sam,然后分两种情况.
- k ≤ W k \le \sqrt {W} k≤W.可以对于每一个字符串 w w w,枚举它的所有子串.复杂度为 q k 2 = W W qk^2=W \sqrt {W} qk2=WW.对于一个 w w w,先枚举起点,再从起点向后扫,维护其在 sam 上的位置.这样可以很方便地求出对应子串在 s s s 出现的次数.找到存在于统计范围内的对应查询区间,计算贡献并加上即可.
- q ≤ W q \le \sqrt {W} q≤W.因为串的数量少,可以每次给出 w w w 后枚举询问.复杂度为 q m = m W qm=m \sqrt {W} qm=mW.对于 w w w 的位置 i i i,处理出子串 [ 1 , i ] [1, i] [1,i] 在 sam 中最长的可匹配后缀对应的 sam 中的位置.在处理右端点为 i i i 的询问 ( l , i ) (l, i) (l,i) 时二分查找最短的满足要求的 sam 的节点位置,即最大化 w [ l , i ] w_{[l,i]} w[l,i] 出现的数量.然后将询问的结果计入贡献即可.
LOJ #6033 「雅礼集训 2017 Day2」棋盘游戏
很经典的博弈论模型:不能走以前删过的点.
抽象成图分类:
- 图不存在完美匹配.这时 Alice 必输.Alice 任意选定一个点,Bob 走其匹配边,然后每次 Alice 走到新的点上后,Bob 都可以走到其匹配的点上知道 Alice 无路可走,游戏结束.
- 图不存在完美匹配.这时 Alice 必赢.Alice 任选一个未匹配点,Bob 无论怎么走都会走到一个被匹配了的点上(不会存在一个连接两个未匹配点的边,不符合最大匹配的条件),Alice 可以走到其匹配点上去.再接下来,会发现 Bob 只可能走到另一个被匹配了的点,否则两人的决策路线就构成了一条增广路,会使得匹配数量变多,不符合最大匹配的条件.
然后发现只有满足一定存在于匹配集合中的点才是 Bob 必胜的,否则是 Alice 必胜.注意到这是一个二分图,我们通过网络流求其最大匹配,检查每个点与 S S S 或 T T T 的连边是否是可行边即可.
P7562 [JOISC 2021 Day4] イベント巡り 2 (Event Hopping 2)
注意到题目中“字典序最优”指的是 k k k 个活动排序后的字典序最优.我们不能按照参加活动的顺序来贪心,而应该按照活动的字典序从小到大的顺序来贪心.我们按字典序从小到大考察每个活动,若当前局面加入这个活动后不会造成已选线段的冲突并且加入这个活动后存在可行解,则加入这个活动.具体来说,将活动抽象成线段,维护已选的线段集合为 S S S.在考察第 i i i 条线段时,若 i i i 和 S S S 中的线段相交,则不能选择 i i i.若不存在相交,我们需要判断加入 i i i 后,整个局面是否还能选择出大于等于 k k k 条线段(是否存在可行解).
设
G
(
i
,
j
)
G(i, j)
G(i,j) 表示
[
i
,
j
]
[i, j]
[i,j] 区间最多可以选择出多少条包含于其中的不交线段.
S
S
S 中线段之间会形成若干个缝隙.选择
i
i
i 之前,整个局面最多可选的线段数量为 S 中的线段数量加上缝隙中最多可选的线段数量.最小的加入
i
i
i 后,本质上是将一个缝隙
[
l
,
r
]
[l, r]
[l,r] 变成
[
l
,
l
i
]
[l, l_i]
[l,li],
[
l
i
,
r
i
]
[l_i, r_i]
[li,ri],
[
r
i
,
r
]
[r_i, r]
[ri,r].最多可选线段数量的增量
Δ
=
−
G
(
l
,
r
)
+
G
(
l
,
l
i
)
+
G
(
r
i
,
r
)
+
1
\Delta=-G(l, r)+G(l, l_i)+G(r_i, r)+1
Δ=−G(l,r)+G(l,li)+G(ri,r)+1.这样我们就可以维护最多可选线段数量了.
现在考虑如何快速求 G ( l , r ) G(l, r) G(l,r) 的值.考虑朴素贪心的过程.找到最小的 r i r_i ri 使得 l i ≥ l l_i \ge l li≥l,将 l l l 变成 r i r_i ri 并重复这个过程.用 ST 表加速,设 s t i , k st_{i, k} sti,k 表示选择出 2 k 2^k 2k 个左端点 ≥ i \ge i ≥i 的不交线段,最大的右端点最小是多少.显然有转移 s t i , k = s t s t i , k − 1 , k − 1 st_{i,k}=st_{st_{i, k-1}, k-1} sti,k=ststi,k−1,k−1.有了 s t st st 的辅助,我们就能快速求出 G ( l , r ) G(l, r) G(l,r) 了.
这启示我们一个连续的过程若可以从中间分开成前后两个独立的过程,并且前一个过程的结束边界可以确定后一个过程的开始位置,那么可以使用倍增优化.
LOJ #6034 「雅礼集训 2017 Day2」线段游戏
李超线段树模版.现在介绍李超线段树,部分内容参考 oi-wiki.
李超线段树用于解决需支持如下操作的问题:
- 增添一条两端点为 ( x 1 , y 1 ) (x_1, y_1) (x1,y1), ( x 2 , y 2 ) (x_2, y_2) (x2,y2) 的线段.
- 询问与直线 x = x 0 x=x_0 x=x0 相交的线段中最高交点的 y y y 坐标是多少.
李超线段树本质上在动态维护凸壳性息.但它使用了类似标记持久化的思想,并且 好像 只能支持 单点查询.
如果服务于单点查询,只需要保证对于任意的单点,答案线段一定包含在线段树的根节点到单点的路径上即可.我们考虑添加一条线段对线段树的更新过程.
首先,一条线段覆盖到线段树上,可以被处理成不超过
log
\log
log 线段树区间的并.对于每个区间,我们单独进行更新.设用于的线段为
d
d
d,线段树区间上原本的线段为
v
v
v,线段树区间左右端点分别为
l
,
r
l,r
l,r,的中点为
m
i
d
mid
mid.若在
m
i
d
mid
mid 处
v
>
d
v>d
v>d.则交换
v
,
d
v,d
v,d.这对于正确性显然没有影响.现在只用考虑在
m
i
d
mid
mid 处
v
≤
d
v \le d
v≤d 的情况了.我们进行分类:
- 在 l l l 处 d > v d>v d>v.将 d d d 递归到区间 [ l , m i d ] [l, mid] [l,mid] 进行更新.
- 在 r r r 处 d > v d>v d>v.将 d d d 递归到区间 [ m i d + 1 , r ] [mid+1, r] [mid+1,r] 进行更新.
- 上述两条均不满足,直接舍弃 d d d 并终止更新.
若满足第 1 或 2 条,另一半的区间 d d d 肯定不优,不用更新.并且第 1,2 条不可能同时满足.这保证这样的更新不超过 log \log log 次.而我们一共会做 log \log log 次这样的更新.添加一条线段的负杂度是 log 2 \log^{2} log2.
P4364 [九省联考 2018] IIIDX
不难想到建树.我们建边 ⌊ i k ⌋ → i \displaystyle \lfloor\frac{i}{k}\rfloor \rightarrow i ⌊ki⌋→i,发现这是一棵以 0 0 0 为根的树.除了 0 0 0 节点,需要给每个节点分配权值,需要保证子树中的权值不小于自己的权值.
很自然地考虑贪心.依编号次序枚举每个节点,发现枚举的点在树上形成根节点的一个联通块.在枚举到 i i i 号点时,二分其分配的权值,check 时检查是否有足够的未被选择的权值使得子树可以得到分配.在选定后,将其子树的预留席位记下来方便 i i i 以后的节点确定权值.
LOJ #6039. 「雅礼集训 2017 Day5」珠宝
首先有平凡的
O
(
N
K
)
O(NK)
O(NK) 的 01背包 做法.
看到
C
i
C_i
Ci 的值域非常小,想到枚举
C
i
C_i
Ci.然后考虑从
C
i
−
1
C_{i-1}
Ci−1 到
C
i
C_i
Ci 的转移.
记 C i − 1 C_{i-1} Ci−1 时的 dp 数组为 f ′ f^{\prime} f′, C i C_{i} Ci 时的 dp 数组为 f f f.我们将 m o d C i ≡ R ( 0 ≤ R < C i ) \mod C_i \equiv R\;(0\le R <C_i) modCi≡R(0≤R<Ci) 的位置单独拿出来进行转移,因为只有 f i − k C i ′ f_{i-kC_i}^{\prime} fi−kCi′ 可以转移到 f i f_i fi.优美的是, f j ′ , f k ′ ( j < k ) f_{j}^{\prime},f_{k}^{\prime}\;(j<k) fj′,fk′(j<k) 作为决策点来说,随着转移位置 i i i 递增, f k f_k fk 的增量一直大于 f j f_j fj 的增量.这意味着一旦 f k f_k fk 优于 f j f_j fj,则 f k f_k fk 之后会一直优于 f j f_j fj.我们一边扫描转移位置 i i i,一边添入决策点 f i ′ f_{i}^{\prime} fi′.维护 p j p_j pj 表示第 j j j 个点的决策点位置.因为上述的性质, f i ′ f_{i}^{\prime} fi′ 的可更新范围是一个后缀区间.而区间的左端点可以通过二分确定.然后我们需要做一个后缀覆盖,故使用线段树维护 p p p.二分加上线段树查询,我们就得到一个时间复杂度为 O ( C K log 2 K ) O(CK\log^2K) O(CKlog2K) 的做法.然后我们使用线段树上二分,就得到了一个优秀的 O ( C K log K ) O(CK\log K) O(CKlogK) 算法了.
等等,是不是哪里不对劲.其实可以直接单调栈.
P2056 [ZJOI2007] 捉迷藏
除了复杂的点分树外,关于 树的形态确定的 树上路径最值类问题有一个十分优雅的做法.
我们依据树的dfs搜索序构造出一个新的 括号序 出来:在搜索进入一个节点时,先依次添入左括号和该节点,接着在搜索完其子树后添入右括号.
code:
int dfc, ty[N], pos[N];
void predfs(int u, int f){
ty[++dfc]=1;
ty[++dfc]=0;
pos[u]=dfc;
for(int v:e[u]){
if(v==f) continue;
predfs(v, u);
}
ty[++dfc]=-1;
}
t y ty ty 表示构造的序列, p o s pos pos 表示树上节点在序列上的位置.
然后这个序列有个很优美的性质.两个节点之间的 未匹配括号数量 就是 节点间简单路径的边数.这个证明是简单的.分类讨论 u → l c a u \rightarrow lca u→lca 和 l c a → v lca \rightarrow v lca→v 两段路径即可,发现不在路径上的括号都被匹配掉了.
就捉迷藏这一例题来看,如何动态维护黑点之间的最长路径呢?我们考虑使用线段树.
先考虑如果知道两个区间的未匹配左右括号的个数,合并的过程:
记
a
a
a 为未匹配右括号,
b
b
b 为未匹配左括号.即区间未匹配的括号形如
)
)
)
)
)
⏟
a
(
(
(
(
(
(
⏟
b
\underbrace{)))))}_{a}\underbrace{((((((}_{b}
a
)))))b
((((((.
有合并后区间的未匹配括号总个数
=
a
l
+
∣
b
l
−
a
r
∣
+
b
r
=
max
(
a
l
+
b
l
−
a
r
+
b
r
,
a
l
−
b
l
+
a
r
+
b
r
)
\begin{aligned} &=a_l+|b_l-a_r|+b_r\\ &=\max(a_l+b_l-a_r+b_r, a_l-b_l+a_r+b_r) \end{aligned}
=al+∣bl−ar∣+br=max(al+bl−ar+br,al−bl+ar+br)
在做区间合并的时候,最长未匹配括号段一定会出现在 左区间内,有区间内,左右区间交界处 三者中的其一.左右区间的答案可以直接继承,而交界处的答案则要通过上面的式子算.具体来说,维护区间的最大前缀
l
1
=
a
+
b
,
l
2
=
−
a
+
b
l_1=a+b,\;l_2=-a+b
l1=a+b,l2=−a+b,最大后缀
r
1
=
a
+
b
,
r
2
=
a
−
b
r_1=a+b,\;r_2=a-b
r1=a+b,r2=a−b.合并区间时活用两区间的
a
,
b
,
l
1
,
l
2
,
r
1
,
r
2
a,b,l_1,l_2,r_1,r_2
a,b,l1,l2,r1,r2 分类讨论取
max
\max
max 的即可.
更新操作稍显复杂,代码如下:
struct stu{
int l1, l2, r1, r2, mx, s1, s2;
//l r 保证一端是合法点
//mx 保证两端是合法点
//s 不做要求
//s1 即 a,s2 即 b
inline stu(int flg=0){
if(flg==0 || flg==-2){
l1=l2=r1=r2=(flg==0)? 0:-inf;
mx=-inf;
s1=s2=0;
}
else{
l1=l2=r1=r2=-inf;
mx=-inf;
s1=(flg==-1); s2=(flg==1);
}
}
inline stu operator +(const stu &o)const{
stu re;
re.l1=max({l1, s1+s2+o.l2, s1-s2+o.l1});
re.l2=max(l2, -s1+s2+o.l2);
re.r1=max({o.r1, o.s1+o.s2+r2, -o.s1+o.s2+r1});
re.r2=max(o.r2, o.s1-o.s2+r2);
re.mx=max({mx, o.mx, r1+o.l2, r2+o.l1});
re.s1=s1+max(o.s1-s2, 0);
re.s2=o.s2+max(s2-o.s1, 0);
return re;
}
};
有一个值得说道的问题就是叶子区间元素赋初值的讨论.
若随便赋初值,会出现非法的子段计入区间的答案.如出现白点点对计入贡献,甚至区间端点不是结点而是括号.
我们回过头来补充维护元素的定义:
- l 1 , l 2 l_1,l_2 l1,l2 需要保证表示的子段右端点为可计入贡献的节点.
- r 1 , r 2 r_1,r_2 r1,r2 需要保证表示的子段左端点为可计入贡献的节点.
- m x mx mx 需要保证表示的子段左右端点都是可计入贡献的节点.
- a , b a,b a,b 无限制,表示整个区间的未匹配右括号和左括号.
然后对于(上文)赋初值的代码就好理解了.
致命缺陷:不能解决树的形态不固定(动态树上)的问题 \red{致命缺陷:不能解决树的形态不固定(动态树上)的问题} 致命缺陷:不能解决树的形态不固定(动态树上)的问题.
P5967 [POI2016]Korale
先将答案的值求出来,再考虑构造方案.
一种想法(与正解无关):考虑加入前
i
i
i 个元素后前
k
k
k 小的值组成的集合
S
S
S.发现新加入一个元素
x
x
x 时,原来不是不属于
S
S
S 的元素,在加上
x
x
x 后仍不可能是前
k
k
k 小.故新的前
k
k
k 小的数只能是
S
S
S 中的数和
S
S
S 中的数
+
x
+x
+x.这样维护的时间复杂度为
O
(
n
k
)
O(nk)
O(nk).
正解的维护方法:将
a
a
a 排序.发现前
k
k
k 小的数是在
a
a
a 的搜索树上的一个联通块.就是说,第
k
k
k 小的数,一定从第
k
′
(
k
′
<
k
)
k^{\prime}\;(k^{\prime}<k)
k′(k′<k) 小转移来的.使用小根堆优先队列
q
q
q 维护搜索树上有用的点,
q
q
q 中元素记两个值
x
,
y
x, y
x,y,分别表示拼出来的数大小为
x
x
x,最后使用的元素位置为
y
y
y.在第
i
i
i 次取出时得到第
i
i
i 小的值.我们将其搜索树上的转移
{
x
+
a
y
+
1
,
y
+
1
}
{
x
−
a
y
+
a
y
+
1
,
y
+
1
}
\{x+a_{y+1}, y+1\} \\ \{x-a_{y}+a_{y+1}, y+1\}
{x+ay+1,y+1}{x−ay+ay+1,y+1}
这两个元素放入
q
q
q 里.
递归证明其正确性(为什么一定可以保证第 i i i 次取出的是第 i i i 小的值):若 i − 1 i-1 i−1 时满足,则前 i − 1 i-1 i−1 小的值都将它们的转移放入了队列中.其中一定存在到第 i i i 小的值的转移.
然后思考构造方案:暴力 d f s dfs dfs,只遍历小于等于答案的状态(并贪心沿着字典序最小),访问的点总个数为 O ( k ) O(k) O(k) 的.使用 s t st st 表加二分或线段数均可以实现快速找到转移.
LOJ#6044. 「雅礼集训 2017 Day8」共
考场上想了一个 O ( n 4 ) O(n^4) O(n4) 的 dp.记 f i , j f_{i, j} fi,j 表示大小为 i i i 的树,深度为奇数的点有 j j j 个点树的个数.然后枚举 i i i,将 1 1 1 的子树和 i i i 的子树拼接起来得到转移方程.
正解:
问题可以转化为求
(
n
−
1
k
−
1
)
∗
F
(
k
,
n
−
k
)
\displaystyle \binom{n-1}{k-1} * F(k, n-k)
(k−1n−1)∗F(k,n−k).其中
F
(
i
,
j
)
F(i, j)
F(i,j) 表示左右节点个数分别为
i
,
j
i, j
i,j 的有标号(给定)完全二部图生成树数量.而上式的组合数是在枚举标号分配.
有结论
F
(
i
,
j
)
=
i
j
−
1
∗
j
i
−
1
\displaystyle F(i, j)=i^{j-1}*j^{i-1}
F(i,j)=ij−1∗ji−1.
证法1:考虑任一棵生成树的 prufer 序列,构造 prufer 时剩余的两个点分别属于左右部,故 prufer
序列中应有
i
−
1
i-1
i−1 个右部点和
j
−
1
j-1
j−1 个左部点.则 prufer 中右部点子序列和左部点子序列的不同数量分别为
j
i
−
1
,
i
j
−
1
j^{i-1},\; i^{j-1}
ji−1,ij−1.而当选定左右部子序列后,prufer 是确定的.故二分图上这样的生成树个数为
j
i
−
1
,
i
j
−
1
j^{i-1},\; i^{j-1}
ji−1,ij−1.
证明2:考虑矩阵树定理.手动消元成上三角矩阵也可得出结论.
#6049. 「雅礼集训 2017 Day10」拍苍蝇
将苍蝇拍中的节点做如下处理:
X
i
←
X
i
−
X
m
i
n
Y
i
←
Y
i
−
Y
m
i
n
X_i \leftarrow X_i-X_{min}\\ Y_i \leftarrow Y_i-Y_{min}
Xi←Xi−XminYi←Yi−Ymin
然后需要求出位于苍蝇拍内的整点有多少个.
判断一点是否在多边形内部常用射线法.从点向一个方向引射线,若与多边形相交的个数为奇数个,则其在多边形内部,否则不在.我们在做此题时不妨引垂直向上的射线.
当然有一些特殊的情况:
在第一种情况中,射线与多边形中的边重合(无法统计交点的个数);在第二种情况中,多边形的边未穿过射线(交点需备算两次).
具体做法:
现特判顶点在多边形内.
我们枚举
x
x
x 坐标,然后将多边形所有边同
x
x
x 的交点求出.对于一特定
y
y
y 坐标,我们只需要观察在其上的交点个数即可.特判多边形中斜率不存在的边(其上的点均为包含的点).对于其他的每一条边,不记其左端点出的交点(这可以解决第二中特殊情况).
然后我们知道了所有在苍蝇拍内的点,记为点集
S
S
S.题目给出来苍蝇的点集
G
G
G.现在统计增量向量
Δ
\Delta
Δ 的个数满足
(
S
+
Δ
)
∩
G
=
∅
(S+\Delta) \cap G = \emptyset
(S+Δ)∩G=∅.容斥可以求
(
S
+
Δ
)
∩
G
≠
∅
(S+\Delta) \cap G \neq \emptyset
(S+Δ)∩G=∅.
设
s
∈
S
,
g
∈
G
s \in S, g \in G
s∈S,g∈G.
s
+
Δ
=
g
Δ
=
g
−
s
s+\Delta =g\\ \Delta=g-s
s+Δ=gΔ=g−s
将
−
S
,
G
-S, G
−S,G 视作两个系数为
0
/
1
0/1
0/1 的多项式.那么有
(
−
S
)
∗
G
(-S)*G
(−S)∗G 中系数不为
0
0
0 的向的个数即为答案.
这道题和 围豆豆 在射线定理的应用细节上都很烦人.
#6048. 「雅礼集训 2017 Day10」数列
神仙做法.
将序列
a
a
a 的翻转序列
a
′
a^{\prime}
a′ 拼接到
a
a
a 前面形成新的序列
b
=
a
′
a
b=a^{\prime}a
b=a′a.例如
a
=
{
5
,
1
,
3
,
2
,
4
}
→
b
=
{
4
,
2
,
3
,
1
,
5
,
5
,
1
,
3
,
2
,
4
}
a=\{5, 1, 3, 2, 4\} \rightarrow b=\{4, 2, 3, 1, 5, 5, 1, 3, 2, 4\}
a={5,1,3,2,4}→b={4,2,3,1,5,5,1,3,2,4}.
然后
b
b
b 的
L
I
S
LIS
LIS 就是
a
a
a 按题意方法操作后的
L
I
S
LIS
LIS.将
b
b
b 的
L
I
S
LIS
LIS 分成两部分,这两部分在
a
a
a 中不交,分别是
a
a
a 中的一个下降子序列和上升子序列,肯定有一种方法将它在操作中模拟出来.然后任意一种对于
a
a
a 的操作方法 可以对应
b
b
b 中一个长度为
n
n
n 子序列.
统计序列
b
b
b 的
L
I
S
LIS
LIS 方案个数
S
S
S.剩余的
n
−
∣
L
I
S
∣
n-|LIS|
n−∣LIS∣ 个点自由选择放置在左边或右边.答案为
1
2
S
∗
2
n
−
∣
L
I
S
∣
\displaystyle \frac{1}{2}S*2^{n-|LIS|}
21S∗2n−∣LIS∣.
??
1
2
\displaystyle \frac{1}{2}
21 怎么来的.我们考虑统计这样的方案时,本质上是枚举了每个点是放在头还是放在尾.第
n
n
n 个数放在头和尾是一种操作.故每种操作其实统计了两遍.
#6045. 「雅礼集训 2017 Day8」价
由于选定药品就必须选定药材且药品的数目必须和药材相等,可以知道会选择出完美匹配中的若干组配对点.从药品到药材建边得到图
G
(
V
,
E
)
G(V, E)
G(V,E),则选择过程为:对于
G
(
V
,
E
)
G(V, E)
G(V,E),导出子图
G
′
(
V
′
,
E
′
)
G^{\prime}(V^{\prime},E^{\prime})
G′(V′,E′) 满足
V
′
V^{\prime}
V′ 是原图若干配对边的邻接点且
G
′
G^{\prime}
G′ 闭合,最大化
∑
u
∈
V
′
V
a
l
u
\displaystyle \sum_{u \in V^{\prime}}Val_u
u∈V′∑Valu.
考虑在
G
G
G 的基础上加一些边.我们先求出一组最大匹配
T
T
T,对于每一种药材建其到
T
T
T 上配对的药品的边.我们在这张图上导出的任意闭合子图的节点都是
T
T
T 的若干组配对点.故我们在上面做最大权闭合子图即可.
P4093 [HEOI2016/TJOI2016]序列
讨论时只需要知道一个位置可能的最大值和最小值即可.
设
i
i
i 位置初始值为
a
i
a_i
ai,可能达到的最小值为
L
i
L_i
Li,最大值为
R
i
R_i
Ri.
考虑以
i
i
i 结尾的 LIS 的前驱位置为
j
j
j.发现需要满足:
a
j
≤
L
i
R
j
≤
a
j
a_j \le L_i\\ R_j \le a_j
aj≤LiRj≤aj
这只考虑了末两项.但是当末两项满足约束后,整个 LIS 在加入 i i i 后同样合法.那么这就是 LIS 的转移条件.我们发现它满足若干组偏序关系.考虑 CDQ.
P3362 Cool loves shaxian
给定
k
k
k,多组询问,每次给定
l
,
r
l, r
l,r 求:
∑
i
=
l
r
I
d
k
(
i
)
\sum_{i=l}^{r}Id_k(i)
i=l∑rIdk(i)
其中
I
d
k
Id_k
Idk 因数
k
k
k 次幂和函数:
I
d
k
(
n
)
=
∑
i
∣
n
i
k
Id_k(n)=\sum_{i|n} i^k
Idk(n)=i∣n∑ik
发现 I d k Id_k Idk 为积性函数(不是完全积性函数).考虑在线性筛时将 I d k Id_k Idk 求出.
- 对于一个质数 p p p,有 I d k ( p ) = p k + 1 Id_k(p)=p^k+1 Idk(p)=pk+1.
- 对于数 p x px px 满足 p ∤ x p\nmid x p∤x. I d k ( p x ) = I d k ( p ) I d k ( x ) Id_k(px)=Id_k(p)Id_k(x) Idk(px)=Idk(p)Idk(x).
- 对于一个数
p
x
px
px 满足
p
∣
x
p\mid x
p∣x.这时我们找到最大的
q
q
q 满足
p
q
∣
x
p^q \mid x
pq∣x.有
I
d
k
(
p
x
)
=
I
d
k
(
p
q
+
1
)
I
d
k
(
x
p
q
)
\displaystyle Id_k(px)=Id_k(p^{q+1})Id_k(\frac{x}{p^q})
Idk(px)=Idk(pq+1)Idk(pqx)
.
那么只有第 3 种情况不好处理.我们维护 g ( x ) g(x) g(x) 表示 x x x 因数中最小的质数出现的幂次.即 g ( x ) = p k ( p k ∈ x , p ∈ p r i m e ) g(x)=p^k(p^k \in x, p \in prime) g(x)=pk(pk∈x,p∈prime).这个在线性筛中是好转移的,因为在线性筛中每个数只会被其 最小的质因数 筛到.
然后第 3 种情况即
I
d
k
(
p
x
)
=
I
d
k
(
g
(
x
)
p
)
I
d
k
(
x
g
(
x
)
)
Id_k(px)=Id_k(g(x)p)Id_k(\frac{x}{g(x)})
Idk(px)=Idk(g(x)p)Idk(g(x)x)
因为有 gcd ( g ( x ) p , x g ( x ) ) = 1 \displaystyle \gcd(g(x)p, \frac{x}{g(x)})=1 gcd(g(x)p,g(x)x)=1,就可以直接转移了.
P5445 [APIO2019] 路灯
考虑某一时刻一盏灯 i i i 灭掉,包含于其的极大联通块 [ L , R ] [L, R] [L,R] 会断裂成两半 [ L , i ] [L, i] [L,i] 和 [ i , R ] [i, R] [i,R].会有怎样的询问受到影响.设询问的起始点和终点分别为 a , b a, b a,b.显然其需要满足 L ≤ a ≤ i , i ≤ b ≤ R L \le a \le i, i \le b \le R L≤a≤i,i≤b≤R.考虑使用 邻接矩阵 的模式去维护通断.将询问以 a a a 为 x x x 坐标, b b b 为 y y y 坐标,抽象到二维平面上.发现上述讨论的情况影响的询问坐标其实包含在以 ( L , i ) , ( i , R ) (L, i),(i, R) (L,i),(i,R) 为左下角和右下角的矩形中.通过对贡献进行容斥,并以此类比一盏灯灭掉的情况,发现修改操作都可以变为矩形加的形式.而询问为单调查.采用树套树或 CDQ 分治都可实现.
以下内容不理性,仅供参考
这启发我们,考察偏序维度时不能仅仅看不等号的个数.应是在确定若干变量后能约束住转移条件时,变量个数才能确定偏序(广义)维数.
foodie (6.21 SX)
可以知道一次操作对于每一种包子的影响是一样的.预处理出对于第
i
i
i 种包子,若只吃它最多能吃的次数,记为
T
i
=
⌊
x
−
w
i
a
i
⌋
+
1
\displaystyle T_i=\lfloor \frac{x-w_i}{a_i}\rfloor+1
Ti=⌊aix−wi⌋+1.
那么决策过程可以看作顺着走(
m
m
m 轮),每到一个地方
i
i
i,若
T
i
T_i
Ti 大于当前吃过的包子数就吃一次,否则跳过.
考察第
i
i
i 种包子在什么时侯最后一次没被跳过是在
k
i
k_i
ki 轮(显然后面每次都被跳过).对于前面的
k
i
−
1
k_i-1
ki−1 轮,显然每种
T
≥
T
i
T\ge T_i
T≥Ti 的包子每次都被吃到.但是对于
T
<
T
i
T < T_i
T<Ti 的包子就不好确定了.我们考虑按
T
i
T_i
Ti 排序从小到大依次求出
T
i
T_i
Ti 的值,这样在计算的过程中
T
<
T
i
T<T_i
T<Ti 部分就已知了.然后考虑第
k
i
k_i
ki 轮.这一轮中,
i
i
i 之前
T
≥
T
i
T\ge T_i
T≥Ti 或
T
<
T
i
,
k
≥
i
T<T_i ,k\ge i
T<Ti,k≥i 的位置会被吃到.在求解
k
i
k_i
ki 时,我们考虑二分答案,检查
m
i
d
mid
mid
位置统计出来之前吃包子的总次数是否
<
T
i
< T_i
<Ti.
难处理的地方在于 k i k_i ki 这一轮的判定中形如 T < T i , k ≥ i T<T_i ,k\ge i T<Ti,k≥i 的偏序条件.在这里可以使用 支持单点修改,矩形查询和 的二维数据结构,如 Bit 套 线段树 等.考场上如是实现,时间复杂度为 O ( n log 3 n ) O(n\log^3n) O(nlog3n),空间复杂度为 O ( n log 2 n ) O(n\log^2n) O(nlog2n).但考虑到其非常不满,在 O2 下可以通过本题.
但是,这种做法仍然有精进的余地.我们考虑给 T i T_i Ti 排序后, k k k 的值有一个很好的性质:记前面 i − 1 i-1 i−1 个数最大的 k k k 值为 p r e k prek prek.那么有当前的 k i ≥ p r e k − 1 k_i \ge prek-1 ki≥prek−1.即不保证第 p r e k prek prek 轮,但前 p r e k − 1 prek-1 prek−1 轮肯定都行.于是就可以消去二分和二维数据结构了.事实上,只需要实现一颗线段树和支持查询排名的 s e t set set 即可.然后就得到了一个时间复杂度 O ( n log n ) O(n\log n) O(nlogn),空间复杂度 O ( n ) O(n) O(n) 的优秀做法.
T344940 Virt
LCT+扫描线.有时间细写.
CF1842E Tenzing and Triangle
场上本来有机会的.
坐标值域为
[
1
,
2
e
5
]
[1, 2e5]
[1,2e5].可以以
x
x
x 坐标作为状态.
记
f
i
f_i
fi 表示覆盖到所有
x
x
x 坐标
≤
i
\le i
≤i 的点需要的最小代价,并且不不使用右端点
x
x
x 坐标
>
i
> i
>i 的三角形.
这样设状态可以充分利用一个非常好的性质:在构造方案时三角形不交更优.这是很显然的.
如图,选择黑色和红色(相交的)两个三角形的代价大于选择绿色三角形.但绿色的覆盖面积包含了黑色和红色.
那么考虑
f
i
f_i
fi 处的转移:
枚举右端点在
i
i
i 处三角形的左端点
x
x
x 坐标为
j
j
j.显然这个三角形可以覆盖
x
≥
j
,
y
≥
y
−
i
x \ge j, y \ge y-i
x≥j,y≥y−i 的所有节点.剩余的点分为两部分:
- x < j x < j x<j.这部分的最小代价是已知的: f j − 1 f_{j-1} fj−1.
- x ≥ j , y < y − i x\ge j, y < y-i x≥j,y<y−i.这部分的节点都需要使用单点删除操作,代价即为 ∑ c \displaystyle \sum c ∑c.在 i i i 确定时,三角形的底边 y y y 坐标确定.
有转移方程
f
i
=
min
j
≤
i
(
A
(
i
−
j
)
+
f
j
−
1
+
s
u
f
c
j
)
=
A
i
+
min
j
≤
i
(
f
j
−
1
−
A
j
+
s
u
f
c
j
)
\begin{aligned} f_i &= \min_{j \le i}(A(i-j)+f_{j-1}+sufc_j) \\ &=Ai+\min_{j \le i}(f_{j-1}-Aj+sufc_j) \end{aligned}
fi=j≤imin(A(i−j)+fj−1+sufcj)=Ai+j≤imin(fj−1−Aj+sufcj)
然后发现随着 i i i 的增大, f j − 1 − A j f_{j-1}-Aj fj−1−Aj 是不变的.只有 s u f c sufc sufc 可能变化.一个点 ( x , y ) (x, y) (x,y) 在 i i i 扫描到 k − y k-y k−y 时第一次被包含在底边内,故我们可以得知每个点对 s u f c sufc sufc 的贡献什么时候撤销.使用线段树动态维护 dp 的过程即可.