202509做题记录
加粗斜体表示思考时被卡了的部分。
打 \(*\) 的表示做过的重新想(做)一遍。
打 \(^?\) 的表示看了题解。
打 ~ 的表示还没做 / 还没写上来。
特别的,\(^!\) 和 \(*^?\) 都表示做过一遍还是不会 😦
按时间查找
如果发现链接是空的,那么请在其他文章里查找。
09/06
9019-R1013-A
9019-R1013-B
9019-R1013-C
9019-R1013-D
AGC008E
AGC009E
ARC205 A~E
09/07
09/08
HT-NOI076-C
ARC204 A~B
09/09
ARC203 A~D
09/10
MX-NOIP-R5A
MX-NOIP-R5B
MX-NOIP-R5C
MX-NOIP-R5D
AGC001 A~F
09/12
AGC002 A~F
P8512
09/13
MX-NOIP-R6A
MX-NOIP-R6B
MX-NOIP-R6C
ARC201 A~B
09/15
HT-NOI077-C
MX-NOIP-R6D
ARC201 C
P11288
P10822
09/16
09/17
MX-NOIP-R7A
MX-NOIP-R7C
MX-NOIP-R7D
HT-CSPS-D
P11930
ARC201 D
09/18
AGC003 A-E
P7560
CF2143 D2
CSAcademy-binary_matching
09/20
ABC424 G
CF2147 A-F
09/21
HT-NOI078-A
HT-NOI078-B
ARC206 A-D
09/24
HT-NOI078-C
CF2150 ABCE1E2
09/25
P11879
CF2150 D
09/26
JOISC2024-Day2 A
ARC101 F
09/28
AGC004 A-D
AGC073 A
09/29
MX-NOIP-R10C
MX-NOIP-R10D
ARC200 D-E
09/30
AGC004 E-F
AGC005 A-C
HT-NOI079-C
校内训练
题单 A
计数
一些数学向的期望题
洛谷 P6835 $^?$
不会做简单题了。
一个想法是,设 \(f(i)\) 表示 \(i\to n\) 期望步数。
然后发现转移很困难。考虑这个问题比较麻烦,利用期望的线性性拆开。
改变定义,设 \(f(i)\) 表示 \(i\to i+1\) 期望步数。 这其实也是期望 dp 的一种套路。
然后转移就是 \(f(i)=\frac{1}{|e_i|+1}\sum_{j\in e_i}(g(i)-g(j)+f_i)+1\)。这里 \(g(i)=\sum_{j<i}f(j)\)。也就是 \(1\to i\) 期望步数。
移项直接做。
套路类 dp
ARC117 E
考虑对前缀和 dp。注意到区间和为 \(0\),相当于前缀和相等。
所以考虑按照前缀和从高到低分层 dp。
考虑当前 dp 到前缀和为 \(i\) 的位置,怎么记录状态和转移。
我们考虑转移是对一些段进行增删合并,这是经典的连续段 dp。
于是我们记录当前的段数 \(k\),用了 \(j\) 个边(对应 \(n\) 个位置)以及已经有 \(s\) 对贡献了。
考虑转移,一个想法是枚举几个段合并,新增几个段。但是这两个操作其实可以合并。我们发现转移本质上是在段之间撒点。
考虑两个段之间新增 \(x\) 个点。\(x=1\) 就是合并,否则相当于在两个段之间加入 \(x-2\) 个新的段。于是最后有 \(x-k\) 个段。
注意到转移和 \(i\) 无关,所以不需要记录 \(i\) 这一维。于是 \(f(j,k,s)\binom{x-1}{j}\to f(j+k,x-k,s+\binom{x}{2})\)。
然后合并正负答案稍微改一下转移就好了。
CF708 E $^?$
几乎想到了。
考虑用一条路径表示一种方案。
一种方法是,考虑从 \((1,1)\) 开始,每次尝试向下走,如果没法走,那么向左右移动到最近可以向下的位置,然后向下。这样的一条路径可以作为一种方案的表示。
然后我们把路径按照:走到当前行,上一行需要向左走、向右走、不动 才能到当前行,分类做 dp。
也就是 \(f/g/h(i,j)\) 表示 dp 到第 \(i\) 行,上一行走下来需要如何走。
这三类各自的限制就是位于当前积木的最右侧、最左侧、(无限制)。
然后上一行的贡献在转移的时候才计算。
转移是好做的。
ARC108 E
还是期望的线性性。
首先因为两个区间之间互不影响,我们套路地考虑区间 dp。设 \(f(i,j)\) 表示选择 \(a_i,a_j\),\([i,j]\) 期望选择了几个。
然后枚举下一次会选择什么,然后根据期望线性性有 \(f(i,j)=\text{avg}_{k\in(l,r),a_k\in(a_l,a_r)}(f(i,k)+f(k,j)-1)\)。
然后直接上数据结构优化。
建立双射 / 转化组合对象
$^!$ QOJ 4890
做这种题感觉一头雾水。。
考虑枚举有多少列填的是 \(1\)。
设 \(c_i\) 表示第 \(i\) 行 \(1\) 的个数。
然后注意到,对于 \(c_j<i\) 的 \(j\),一定有 \(0\) 元素的列填的是 \(1\),所以这些行都要填 \(0\)。
同理,\(c_j>i\) 的行 \(j\) 填 \(1\)。
然后考虑 \(c_j=i\)。显然对于 \(c_x=c_y=i\),需要满足 \(row_x=row_y\)。否则会有矛盾。
然后分类讨论:
- 不存在 \(c_j=i\),那么去掉必须填 \(0/1\) 的列,在满足有 \(i\) 个 \(1\) 的基础上随便填。组合数。
- 存在 \(c_j=i\),要求 \(\forall c_x=c_y=i,row_x=row_y\),然后判定是否合法。如果合法,方案数就是 \(c_j=i\) 的 \(cnt\) 个行随便填,也就是 \(2^{cnt}\)。
ARC186 D $^?$
从后向前考虑,我们发现就是要求后缀的 \(\sum 1-a_i>0\)。
把移动 \(i\) 看做向上,\(1-a_i\) 看做向右,这就是不能越过直线的格路计数,如果没有 \(a_i\) 的限制可以卡特兰数。
然后考虑计数。问题在于不能有 \(a_i\) 的限制。考虑字典序,我们只要枚举第一个比要求小的位置,以及这个位置的值,后面就没有限制了。
复杂度不是 \(O(\sum a)=O(n^2)\) 的。注意到格路计数的终点是 \((n-1,n-1)\),所以当 \(\sum a\geq n\) 时,贡献为 \(0\),所以只需要 \(O(\min(n,\sum a))=O(n)\)。
然后有一些细节需要注意。
AGC009 E $^?$
首先把做平均数的操作看做一棵树,然后考虑增加要求,使得不同的满足要求的树,所对应的答案不同。
首先,如果一层有 \(\geq k\) 个 \(1\),那么可以“合并”,所以要求一层的 \(1\) 只能 \(<k\) 个。这里也可以看做是一个 \(k\) 进制小数的进位操作。
然后考虑计数答案。我们枚举这种树的树高 \(h\) 和 \(1\) 的个数 \(c\) 是多少。
然后考虑如果有 \(t\) 次进位,那么 \(h=d-t,c=m-(k-1)t\),其中 \(d\) 是操作次数,即 \(d=\frac{n+m-1}{k-1}\)。
为什么这是对的。注意到 \(h\) 是有 \(c\) 个 \(1\) 的情况下,树的最大深度,一种方案一定可以唯一变成这样的 \(h\)。
Dp 类计数
AGC008 E $^?$
注意到这个基环树森林只有:奇环,偶环,环上每个点最多延伸一条链构成。
考虑奇环可以有两种方法,一种是 \(q_i=p_i\),一种是 \(p_i=q_{q_i}\)。
奇环偶环还有一种方法是,把两个长度相同奇(偶)环拼在一起,那么有 \(p_i=q_{q_i}\)。
然后是环上每个点延伸链。
考虑只能把这个环按进置换环上,然后根据链的长度,方案有 \(0/1/2\) 种。分类讨论一下。
计数只要随便枚举某种方案的个数就好了。代码细节比较多。
题单 B
数据结构
P13663
观察可得,其实 \(f(i)\) 就是 \(i\) 子树的最大深度。
然后考虑增加叶子的贡献。我们设 \(F(x,d)\) 表示在加入 \(F(x,d)\) 时,\(f(x)\) 第一次 \(=d\)。
然后我们在转移 \(F(x,d)\) 的过程中顺便计算贡献。
具体地,对于一个点 \(x\),有 \(F(x,0)=x\),然后先假设 \(rt-x\) 路径上的 \(f\) 都 \(+1\) 了,此时贡献 \(dis(1,x)\)。
转移有 \(F(x,i)=\min_s F(s,i-1)\)。然后对于没有被选上的 \(F(s,i-1)\),意味着在加入这个叶子的时候,\(f(x)\) 不变,所以贡献到这里截止,故 \(F(s,i-1)\) 这个点的贡献减掉 \(dis(1,x)\)。
然后考虑怎么转移。这样一维和深度有关的,使用长剖做到 \(O(n)\)。
P11210
厉害观察。想了半天。
我们考虑直接计数一定是只能二维数点。
但是这道题的特点是,我们只关心最优的点,所以可以放宽限制,只要保证多算的点不会成为答案。
考虑把图画出来,我们发现,只有在第一个合法点上方的不合法点会影响答案,所以我们只要找到第一个不合法点就好了。
P10805
超级构式。
路径问题,考虑点分治,假设当前根为 \(x\),对于路径 \(\to x\) 的部分做 dp,求出在 \(i\) 加油的次数 \(f(i)\),然后对于 \(x\to\) 的部分,同样是求出在 \(i\) 加油的次数 \(g(i)\)。
\(f\) 是向祖先转移,\(g\) 的转移是从祖先计算得到。
为了 \(x\to\) 部分的计算,需要记录的还有 \(\to x\) 的所有路径在 \(x\) 的油量。
这些东西统统在 vector 上二分就好了。细节一万个,代码一大坨。
QOJ 9634 $^?$
考虑区间做 \(\max_{j=l}^i a_j\) 这样的操作,也就是前缀 \(\min/\max\),典型做法就是单侧递归线段树。
然后 pushdown 的时候,我们发现操作可以统一为:对当前节点 \(x\) 的每个位置 \(i\) 加上 \(c\max(v,pre_i)\),其中 \(pre_i\) 是 \(i\) 在当前节点的前缀最大值。
于是我们每个节点 \([l,r]\) 维护 \(v_i,c_i,d_i\) 表示真实 \(b_j=b_j+d_i+c_i\max(v_i,a_l,\cdots,a_j)\)。
考虑这个操作 \((x,v,c)\) 怎么做:
对 \(v\) 和 \(\max_{i\in ls}a_i\) 的大小讨论:
- 如果 \(v\) 更大,那么左侧已知 \(d_{ls}\gets c_iv_i\),右侧递归处理。
- 否则 \(v\) 较小,那么对于右侧,\(v\) 已知,所以对于右侧打 tag \(g\),对于左侧递归。
这里是 \(\log^2\) 的。
然后考虑 pushdown 的过程:
- 对于 \(d\),类似区间加标记的下放。
- 对于 \(g\),考虑左侧兄弟节点 \(L\) 的 \(\max a\) 已知,所以直接操作 \((x,L,g)\)。
然后考虑打 tag 的时候需要维护子区间的和,所以在 pushup 的时候记得计算一次 \(g\) 会让节点子区间 \(b\) 的和增加多少,这里的逻辑和 \((x,v,c)\) 操作的逻辑差不多。
还有一种做法:
考虑离线扫描线。
考虑把时间轴竖起来,用线段树维护。
考虑对于查询操作,做差分之后就变成历史和。
\(a\) 的修改,放到扫描线上,变成了时间轴被分成了几段,每段的 \(a\) 是相同的。这样只要在线段树上做区间操作就好了。
然后是 \(b\) 的修改,我们发现,这个操作可以用历史和维护,对于现在点亮的修改,相当于维护 \(\max a\) 的历史和。
考虑修改 \(\max a\) 就是吉司机线段树,然后历史和维护一个 \((k,b)\) 表示当到 \(t\) 时刻时,历史和是 \(tk+b\)。
然后吉司机线段树的用处就是把 chmax 变成区间加 \(z\),这样历史和 \((k,b)\to(k+z,b-tz)\)。
需要注意的是,我们操作的都是区间 \(\min\),所以 pushdown 的时候需要子区间的 \(\min\) 是当前区间的 \(\min\)。
QOJ 8229 $^?$
同样是离线扫描线。然后此时相当于要维护单个栈的 push,pop 的结果。
正着做不太好维护一个元素是否最后仍然存在。
所以倒着做。此时我们知道右侧的 pop 次数,所以一定知道当前元素是否留存。
然后这种当前情况和之前有关系的,仍然是考虑单侧递归线段树。
我们维护当前节点有 \(b\) 次 push,一开始的 pop(未消耗的)有 \(a\) 次,当前区间的和 \(s\)。对于叶子,记录 \(w\) 表示 push 的元素。
然后对于一次修改是单点操作。
考虑怎么维护,\(a,b\) 是好维护的。对于 \(s\),需要递归求。
考虑 \((x,v)\) 表示 \(x\) 节点右侧 \(v\) 个 pop,最后的答案。
仍然是对 \(v\) 和 \(b_{rs}\) 分类讨论。这是简单的。
考虑查询怎么做。为了方便,我们做差分,先提取出 \([1,p]\) 对应线段树上的区间,处理出区间右侧的 pop 个数,然后从左到右枚举。
QOJ 4780
考虑查询是全局的,所以考虑一次操作对全局的影响。
具体的,我们求出这次操作 push 的 \(x\) 最久可以保留到什么时候。
直接无脑分块。
考虑整块,对于块 \([l,r]\),我们要求的是如果 \([l,r]\) 在 \(t\) 时刻 push 了一个元素,什么时候会彻底消失。记答案为 \(ans(l,r,t)\),我们发现,\(ans(l,r,t)\) 随着 \(t\) 是单调的。所以双指针维护。
具体的,把一次操作看做区间 -1,每个位置初始有 sz 的值,要求 \(\min\geq 0\)。
然后用分块维护答案。如果操作包含整块就打标记,否则暴力做。我们发现操作放到分块上也是 \(O(n\sqrt n)\) 次,所以复杂度均摊正确。
对于散块,对操作序列分块,然后类似的双指针就好了。
复杂度单根号。
集合幂级数
LOJ 154 $^?$
集合幂级数 \(\exp_{\leq k}\) 板子。
前置知识:多项式 \(O(n^2)\) 快速幂。
考虑 \(\displaystyle\exp_{\leq k} A=\sum_{i=0}^k \frac{A^k(x)}{k!}\)。
令 \(B(x)=\exp_{\leq k}A\),则:
然后求出 \(\displaystyle C=\frac{A^k(x)}{k!}\),然后根据加法卷积,有:
注意 \(\displaystyle[x^0]B(x)=\sum_{i=0}^k\frac{([x^0]A(x))^k}{i!}\)。
P13843 $^?$
考虑 exp 可以看作是每次钦定最高位做子集卷积的方案数,这样计数就是不重不漏的。
也就是考虑 \(B=\exp A\),那么可以递推 \(\displaystyle B_S=\sum_{T\subseteq S,\max T=\max S}B_{S-T}A_T\)。
然后考虑修改一下子集卷积的实现以满足要求。
考虑处理出 \(C(S,c)=\sum_{T\subseteq S,\max T=\max S}A_T\)。
然后考虑按照 popcount 从小到大转移。
我们让 \(f(i,S)\) 表示 FWT 后,转移的 \(\sum x=i\),其中 \(x\) 是 FWT 后所在的 popcount 值。且 \(\max \bigcup T=\max S\) 的情况的 \(f(i,S)\) 的答案。
假设当前枚举到 \(\text{popcount}=i\) 的情况,那么求出 \(y_S\) 表示 \(\text{popcount}(T)\leq i\) 的情况的组合方案数。
然后转移有 \(f(i+j,S)=C(j,S)y_S\)。
P9333 $^?$
考虑删去了 \(S\) 后,哪一些位置 \(T\):当前仍然被选,但是再被删一次就不能被选。
于是答案只和 \(S,f(T,S)\) 有关,其中 \(f(T,S)=\max_{A\in \{a_n\}\neq S}|A\cap T|\)。
然后我们只要记录 \(f(T,1/2)\) 表示 \(f(T)\) 取到最大和次大时,\(S\) 是多少。然后考虑怎么维护。
我们考虑 \(A\cap T\subseteq A,T\),所以我们先高维后缀和,然后再高维前缀和。
第一次就是 \(A\to A\cap T\),第二次就是 \(A\cap T\to T\)。
题单 C
图论找性质
P8456
很牛的题。
考虑正难则反,问题变为计数不合法的方案数。
分类:
-
\((x,y)\) 间的路径只有经过 \(0\)。
这种情况稍微画一画就可以发现,这要求 \(x,y\) 所处点双不能有 \(1\)。更准确的说,\(x,y\) 在圆方树上经过的方点所代表的点双不能有 \(1\)。
-
\((x,y)\) 间的路径只有经过 \(1\)。同理。
-
\((x,y)\) 间的路径经过 \(0,1\),且不存在一条路径同时有 \(0,1\)。
我们直接猜测答案一定是在同一点双中才有可能。进一步的,当且仅当 \(x,y\) 是点双中唯二的有不同类型出边的点,\((x,y)\) 才满足这种情况。
然后直接硬写。
树上数据结构
训练赛 0903
P6715
很牛的题。考虑我们只关心向一个位置集兵最多可以有多少个人,所以我们只要 dp 限制这个条件就好了。
于是我们考虑 \(f(i,j)\) 表示 \(i\) 位置最多集兵 \(j\) 个人的情况下前 \(i\) 个位置最多可以放多少人。
然后去分类讨论:
- \(f(i,j)+k\to f(i+1,k)(j<a_i,k<b_i)\)
- \(f(i,j)\to f(i+1,j-a_i)(j\in[a_i,a_i+b_i))\),注意此时 \(i+1\) 一开始不能有数,否则 \(f(i,j)\) 限制不满足。
- \(f(i,j)\to f(i+1,j)(j\geq a_i+b_i)\)
- \(f(i,j)+b_i\to f(i+1,k+b_i)\)
复杂度 \(O(nA)\)。
训练赛 0906
B
考虑从大到小考虑小组。
考虑令 \(f(i,j)\) 表示 dp 到大小为 \(i\) 的小组,当前还有 \(j\) 个允许加入大小 \(\geq i\) 的小组的人没有被选的方案数。
然后枚举有 \(k\) 个大小为 \(i\) 的小组,有 \(f(i,j)=\sum_k f(i+1,j-a_i+ik)\binom{j-a_i}{ik}\dfrac{(ik)!}{i!^kk!}\)。
复杂度 \(O(n^2\log n)\)。
D
一开始的思路就是你找到这个船的一个关键点,也就是对称轴和横向最长轴的交点。
然后相当于询问关键点走几步可以离开图 \(G'\)。
这里这个图的限制就是:如果关键点可以到达这个位置,那么关键点所代表的那个船的位置在原图上不可以包含障碍物。
你对于每一行你去考虑这一行上的障碍,会对整个 \(G'\) 造成什么影响,然后你去枚举每一列,发现对这一列的影响是,会对这一列的一个连续段做一个禁止到达的限制。
然后这个东西就是做一个差分,然后最后前缀和一下。
要注意的就是你最后判定离开图的边界不一定是一个矩形。
MX
NOIP R3
C
注意到答案一定是 \(|LIS|+|LDS|-[\exists LIS,LDS\text{ s.t. } LIS\cap LDS=\varnothing]\)。
然后枚举 \(LIS,LDS\) 的交点,计算方案数,和总方案数比较一下,如果不一样就说明存在不交的情况。
NOIP R4
D
大力上树剖。
首先是 \(op=4\),实现函数 pos(x,y,z) 表示答案。这个的意义是 \(x,y,z\) 的路径交点。
然后是 \(op=2\),考虑 \(z\) 在 \(y\) 子树中的情况(其他情况是简单的),答案在 \(y-z\) 路径上的点,用树剖维护,答案为 \(z\) 的只要快速求出 \(z\) 子树的大小就好了。
然后是 \(op=1\),只要在 \(op=2\) 的基础上维护以 \(x\) 为根时,询问 x z z 1 的答案。
NOIP R5
A
考虑对每一轮的期望贡献求和即为答案。即 \(ans=\sum_i(S_i=\mathbb{E}[\max(0,D_i)])\)。
然后把 \(a_i\) 表示第 \(i\) 轮 \(1\) 号选手的观众数。注意到 \(D_i\) 和 \(a_i\) 可以互推。然后考虑递推,从 \(i-1\to i\)。
于是我们要求的是 \(S_i=\mathbb{E}[\mathbb{E}[\max(0,D_i)|a_{i-1}]]\)。
然后考虑 \(u_i,d_i\) 表示第 \(i\) 个人被选上/没选上的概率,有 \(u_i=\frac{a_{i-1}}{n+i-1},d_i=1-u_i\)。
然后有 \(S_i=\mathbb{E}[\mathbb{E}[\max(0,D_i)|a_{i-1}]]=\mathbb{E}[u_i\max(0,D_{i-1}+1)+d_i\max(0,D_{i-1}-1)]\)。
然后把 \(u_i,d_i\) 用 \(D_i\) 替换,得到 \(g(D_{i-1})=\frac{a_{i-1}}{n+i-1}\max(0,D_{i-1}+1)+\frac{n+i-1-a_{i-1}}{n+i-1}\max(0,D_{i-1}-1)\)。
对 \(D_{i-1}\) 分类讨论以把 \(\max\) 拆开。
于是 \(\mathbb{E}[g(D_{i-1})]=\mathbb{E}[2D_{i-1}(n+i)[D_{i-1}\ge 1]+(n+i-1)[D_{i-1}=0]]=2(n+i)\mathbb{E}[D_{i-1}[D_{i-1}\ge 1]]+(n+i-1)\mathbb{P}(D_{i-1}=0)\)。
然后就是求 \(\mathbb{P}(D_{i-1}=0)\)。这种东西是简单的。注意到 \(t\) 时刻 \(1\) 选手有 \(k\) 个观众的概率是 \(\frac{\binom{t-k+n-1}{n-2}}{\binom{t+n-1}{n-1}}\)。然后做完了。
还有一种做法是,枚举 \(t\) 时刻每种情况,求出概率,贡献,直接化式子。还没推。
C
多串出现在一个串中的次数,考虑 ACAM。然后问题就是这个串经过 ACAM 每个点的次数。
注意到这个串有很优良的性质,我们用 \(A(x,d)\) 表示序列 \(f(0)+d,f(1)+d,\cdots f(k^x-1)+d\),我们有 \(A(x,d)=A(x-1,d)A(x-1,d+1)\cdots A(x-1,d+k-1)\)。
这个性质启发我们通过图表示子结构关系,然后跑一个 DAG 路径数就好了。
我们还要维护这个串在 ACAM 上的跳跃情况,所以需要记录 \(B(x,d,S)\) 表示序列进入时位于 ACAM 的 \(S\) 节点,出来的时候位于 \(B(x,d,S)\)。
计算当然是类似的。分析状态数是 \(O(k(\sum|p_i|)\log_kV)\),并且非常跑不满。
在求解的时候顺便连边,最后跑一个 DAG 上求路径数的东西就好了。
D $^?$
点击即得 A 性质的 50pts。随便构造。
正解有点困难,所以借了一些题解的内容。
正解考虑递归构造。我们尝试每次删去两个点染成同一种颜色,这样新的树的限制一定更严格。
然后考虑怎么找到这两个点。
我们考虑满足 \(\deg b+\deg d\leq n+1\) 的 \((a,b,c,d)\) 是好的。
考虑 \((a,b,c,d)(e,f,g,h)\),若 \(b,d,f,h\) 不同,则至少存在一个好的元组。
如果 \((a,b,c,d)\) 是好的,并且按顺序构成长度为 \(3\) 的路径,那么此时可以删去 \(a,d\),并且把 \(d\) 的子树合并到 \(b\) 处。(这就是要求 \(\deg b+\deg d\leq n+1\) 的作用)。
考虑怎么找到这种东西。分类讨论。考虑我们找到了 \(x,y\) 使得 \(dis(x,y)>2\)。
考虑 \(path(x,y)=a_1a_2\cdots a_m\)。其中 \(a_1=x,a_m=y\)。
- 如果 \(m\leq 5\),则删去 \(x,y\)。
- 否则,必有一个成立:
- \((a_1,a_2,a_3,a_4)\) 是好的。按之前方法操作。
- \((a_m,a_{m-1},a_{m-2},a_{m-3})\) 是好的。按之前方法操作。
- \(m=7\) 且 \(\exists z,(z,a_4)\in E\)。删去 \(x,z\)。
然后代码也挺麻烦的。。
NOIP R6
A
简单区间 dp。\(f(l,r,x)\) 表示 \([l,r]\) 是否满足句法 \(x\)。
转移枚举 \(k\),有 \(f(l,r,p_w)\gets f(l,k,q_w)\land f(k+1,r,r_w)\)。
复杂度 \(O(n^3m)\)。
B
首先有第 \(k\) 大子集有一种 \(O(k\log k)\) 求法。
考虑我们需要让大小为 \(sz\) 的集合被大小 \(\leq sz\),且和更小的集合增广。这样可以保证一定可以被正确访问到。
怎么做呢。我们考虑这么一个策略:对于一个集合 \(S\),我们找到最大元素 \(x\),有两种增广方法:
- \(S\cup\{x+1\}\)
- \((S\setminus\{x\})\cup\{x+1\}\)
这样,我们发现一个集合一定可以被这样正确的增广到。
所以正确性和复杂度正确。
这道题要求多个集合的第 \(k\) 大子集,并且要求子集大小 \(\geq 3\)。
对于第一个限制,随便再套一个 pq 就好了,对于第二个限制,我们猜测我们不计算增广到的 \(sz<3\) 的集合,但是继续增广,复杂度是对的。然后就过了。
要注意到的是,因为集合有负数,所以需要特殊处理。做法是考虑把负数取反,然后相应修改对应的操作。
写起来依托。
C
水题。考虑树怎么做。显然是一个 \(V\) 形的函数。
然后多棵树做闵可夫斯基和还是一个 \(V\) 形。
然后考虑最后一步是把环也算上。
一共三种情况。设 - 有 \(c1\) 个,+ 有 \(c2\) 个。
- \(c1\geq 2\),那么代价随着新增子集数的变化是 \(c1,c1-2,c1-3,\cdots,0,1,\cdots c2\)。
- \(c1=1\),那么代价随着新增子集数的变化是 \(1,1,2,\cdots c2\)。
- \(c1=0\),那么代价随着新增子集数的变化是 \(0,2,3,\cdots c2\)。
前两种情况直接正常闵可夫斯基和,最后一种情况注意到不是下凸的,但是斜率一开始就是 \(2\),所以一定是最后选的,接在最后面就好了。
D $^?$
这么简单的题不会。唐。
考虑在 LCA 处(\(x\))统计贡献。
考虑轻子树对轻子树的贡献。Trie 维护 \(a_u\oplus w(dep_x)\),然后暴力做。
考虑重子树对轻子树的贡献。递归进重子树可以得到重子树的 \(a_i\) Trie,对每个轻子树的 \(u\),查询异或 \(w(dep_x)\oplus a_u\) 的最小值。
这里 Trie 需要做线段树合并来维护。
考虑轻子树对重子树的贡献。我们在递归进去之前,先把轻子树的 \(a_u\oplus w(dep_x)\) 加到一个 Trie 中,然后对于当前递归到的位置,查询异或上 \(a_x\) 的最小值。
复杂度 \(O(n\log n\log V)\)。
NOIP R7
B
幽默。考虑从后到前维护限制。
限制形如 \(\sum_{j\leq r}[s_i[j]=c]=g(i,r,c)\)。
分类讨论:
-
如果 \(|s_i|<|s_{i+1}|\),那么考虑限制 \(g(i+1,r,c)\):
因为 \(s_{i+1}\) 是 \(s_i\) 拼接的前缀。不妨设 \(s_{i+1}[1:r]={s_i}^k+s_i[1:p]\),其中 \(k=\lfloor r/|s_i|\rfloor,p=r\bmod |s_i|\)。
然后发现,有限制转移 \(g(i,p,c)=g(i+1,r,c)-k\times cnt_i(c)\)。也就是把 \({s_i}^k\) 扣掉,对 \(s_i[1:p]\) 做限制。
-
否则 \(|s_i|\geq |s_{i+1}|\)。
那么有 \(g(i,r,c)=g(i+1,r,c)\)。
所以发现,转移其实就是 \(g(i,p,c)=g(i+1,r,c)-k\times cnt_i(c)\)。
然后注意到一个位置最多一个限制,所以限制个数只有 \(O(\sum |s_i|)\) 个。
因为我们只要求出 \(s_1\),满足限制随便构造就好了。
复杂度 \(O(|\Sigma|\sum|s_i|)\)。
Fun fact:原题数据没有 \(|s_i|>10^6\) 的情况。
D
把树看做原树上的点 \(i\) 挂了一条长为 \(a_i\) 的链。
本质上是求的树的半径,我们直接看做树的直径 \(d\),然后答案即为 \(\lceil\frac{d}{2}\rceil\)。
也就是求 \(\min_i\max_ja_i+a_j+d(i,j)\)。这是有问题的,因为直径折半可能在 \(a_i\) 链上。然后注意到此时 \(d<2a_i\),所以我们让直径要 \(\geq\max a_i\)。
有一个性质:
在一颗树上,合并两颗虚树的新虚树的直径,仍然满足直径的两端点在原来两颗虚树的直径端点中。
然后有了这个性质,做题就简单了。我们考虑用线段树维护 \([l,r]\) 中的点(及其链)的虚树的直径。
为了规避之前那个问题,一开始我们在线段树上预处理一个点(及其链)的直径为 \(2a_i\)。
然后修改需要求 \(O(\log)\) 次 LCA。
复杂度 \(O(n\log n)\) 或 \(O(n\log^2 n)\)。
NOIP R9
C
比较麻烦。考虑求出 \(F_x\) 表示直径为 \(x\) 最多可以加几个叶子。
既然最后的直径不知道是什么,所以直接考虑枚举直径中点,然后求得就是 \(cntleaf\times len-dis\)。
换根 dp 可以求出上述值。然后考虑每次让直径加二,需要的代价是 \(cntleaf\)。
然后注意到 \(dis\leq n\),所以我们预处理出 \(\leq n\) 的答案,更大的就是加上差值。
NOIP R10
C $^?$
考虑跨过一条线的匹配,我们发现,如果有匹配,若匹配左侧的元素集合(不是位置集合)为 \(L\),右侧为 \(R\),那么 \(|L|=1\) 或 \(|R|=1\)。
这个性质非常强,我们考虑 dp。
要根据限制去设计 dp 的状态。
我们在从左到右枚举到第 \(i\) 个位置的时候,维护是 \(|L|=1\) 还是 \(|R|=1\),以及这个集合唯一的元素是什么。
然后转移是简单的。要注意的是,可以从 \(|L|=1\) 转移到 \(|R|=1\) 的情况。匹配形如一个前缀相同,一个后缀相同,但是不交。
D $^?$
遇到像:排列变成逆排列,有一个套路是,把二元组 \((x,y)\) 表示 \(a_x=y\),那么求逆就是交换 \(x,y\)。
这个东西的用处在于,可以更直观的表示当前排列的状态,并且如果求逆是全局操作,就可以预处理出所有交换后的情况。
太牛了。又是没见过的套路。。
考虑维护四元组 \((x,y,a_{x,y},b_{x,y})\),然后发现几个操作无非就是单点加或者交换两个位置。同时 \(b_{x,y}\) 不会动。
我们关心的是交换的情况。于是我们预处理出 \(3!=6\) 种交换的情况,对于每种:
考虑怎么判定一行是否合法。
对于这种神秘的恰好 \(k\) 次的限制,可以考虑和哈希。
我们考虑对于第 \(i\) 行,枚举位置 \(j\),如果有 \(b_{k,j}=b_{i,j}\),那么 \(v_i\gets v_i+w_k\),其中 \(w_k\) 是随机数。
那么满足要求,当且仅当 \(v_i=k\sum_{j\neq i}w_j\)。
注意到影响答案的只有四元组第一个位置的变化(行的循环移位),所以我们考虑 set 维护,满足要求的原来行的位置,然后求答案在 set 上二分即可。
HT
NOI 076
A $^?$
先建出表达式树,这个表达式树的儿子根据交换律,是可以自由交换顺序的。
首先有一步很厉害的观察:对于表达式树上相邻的两个相同符号的点,可以直接合并。最后得到的新表达式树的儿子仍然可以自由交换。
这一步是全题的关键。
然后接下来是比较顺利的。
考虑 dp,注意到两个子树合并最多把答案减 1,所以我们只关心子树答案最小的情况。我们只要维护子树内表达式首位的字符可能是什么即可。
然后考虑转移。
考虑让答案减少 1,只有可能是一个子树和一个叶子合并的时候出现。
例如子树是 \(\texttt{ab+}\),叶子是 \(\texttt{a}\),这样合并后(\(\texttt{aab+}\))答案减少 1。
首先贪心合并叶子。然后只有可能是非叶子和叶子合并。这就是经典二分图匹配。
于是答案就是子树答案之和(减叶子合并)减去二分图最大匹配。
然后考虑首位字符 \(c\) 的可能性。
这说白了就是钦定某个子树的首位是 \(c\),二分图最大匹配是否不变。分类一下:
- 存在叶子 \(c\),那么显然可以放在开头。
- 不存在叶子 \(c\)。具体的,我们求出原来的最大匹配,然后尝试对“叶子”(实际不存在) \(c\) 做匹配。如果可以匹配(存在一颗子树可以被拎出来做开头),那么就成功。
B
原:AT_xmascon22_f fast as fast as ryser。见 202508 做题记录。
C $^?$
非常牛的观察性质。
首先有一个贪心,我们把每辆车分成 \(k+1\) 的一堆整块和最后的小块。然后枚举块,每次选择性价比 \(\frac{(s+1)c}{s}\) 最优的(最小的)选择整块,不能超过总人数。设最后大小为 \(i\) 的块选了 \(k_i\) 个:
那么,最后大小为 \(i\) 的块的前 \(\max(0,k_i-k)\) 个块一定在最后答案中。 这其实也是这种数据范围巨大的题的一个很好的猜测方向。
然后我们只要对剩下的 \(\sum_{i=1}^k ki=O(k^3)\) 个人做暴力 dp 就好了。
然后对于每种大小只要保留 \(k^3/sz\) 个,枚举个数转移,转移可以做到 \(O(k^6\log k)\)。
NOI 077
A
考虑把字符串放到 Trie 树上,那么问题就是求路径左侧的字符串个数。
如果没有修改,可以直接预处理。复杂度 \(O(|\Sigma|\sum |s_i|)\)。注意空间复杂度需要稍微控一下。
有修改,我们发现,对于修改不会合并字符的,我们一开始就可以预处理出来:\(g(s,t)\) 表示,如果 \(rk(s)>rk(t)\),那么字符串的 rk 会上升 \(g(s,t)\) 个。
问题在合并字符。注意到合并最多 \(|\Sigma|\) 次,每次直接 rebuild 整个 Trie,那么复杂度是 \(O(|\Sigma|^2\sum |s_i|)\)。
B
我们考虑枚举直径的中点。注意,如果有 \(0\) 权边,那么中点可以是一条路径。
对路径的限制计数(进一步的,连通块限制的计数也可以如此)可以考虑点减边容斥,也就是在路径内的点的答案和减去在路径内的边的答案和。
然后注意到,同时还有一种情况,直径中点在边上,所以这种情况和之前“减去在路径内的边的答案和”消去了。
然后接下来只要求以点为中点的的方案数之和。
然后直接 dp,有 \(f(i,j)\) 表示 \(i\) 点深度为 \(j\) 的方案数,以及所有直径之和。
注意到 \(f(i,*)f(j,*)\) 的合并是 \(O(|f(i)|+|f(j)|)\) 的。于是我们按照子树深度排序合并,总复杂度是 \(O(n^2)\) 的。
NOI 078
A
dp 是简单的。
然后注意到矩阵快速幂的复杂度依赖于矩阵大小,但是子树个数很多,怎么办?
考虑 dp 优化的方向是,把相同的转移合并在一起。 我们发现相同的转移就是子树大小相同。
有套路:和为 \(n\) 的正整数序列,不同元素个数有 \(O(\sqrt n)\) 种。
于是矩阵快速幂的矩阵大小 \(O(n^{0.5})\),复杂度是 \(O(n^{1.5}\log V)\)。
如果没观察到,怎么办?考虑矩阵转移的 dp 可以写成线性递推形式,所以我们直接平方求出线性递推式,然后远端求值。复杂度平方。
* B
对于这种局部最值的问题,考虑笛卡尔树上 dp。
注意到一个子树内的失效时间是左右两个子树内失效时间 \(x,y\) 的最大值加上 \([x=y]\)。
当然,如果某个子树是边界,那么边界那一侧的子树视作始终存在,所以值是 \(y+1\)。其中 \(y\) 是没有边界的那一侧子树。
于是 dp 的转移考虑维护是否一侧是边界,分类转移。
注意计算答案的时候要特殊处理。
NOI 079
B $^?$
一种方式是 dp 启发正解。注意到 dp 要维护的是一列的值,然后状态的转移构成三元无向环,然后组合计数小练习。
当然,你也可以变身功力深厚的计数 master,huang,一眼瞪出来。
~ C $^?$
字符串还是太难了。
考虑暴力。做法是求出 lcp,然后根据 \(l_i,l_j\) 分类讨论,看是否存在一个合法修改点使得在所有区间的交中。
正解无非就是慢慢去拆这个限制。 TODO。
CSP S
Fun fact
比赛一开始的时候,S 组的题放成了 J 组,以至于我做完 J T2 想要交题的时候,发现交不了,刷新一下才发现换题了。C
简单 dp。注意到要求就是相邻的两个同色位置,中间必须要有一个异色的更大点。
然后设 \(f(i,j,l,r)\) 表示区间 \([i,j]\) 左右两侧为前(后)缀最大值的颜色集合。然后每次枚举最大值的位置坐转移即可。
D $^?$
难点在于想到猫树。
注意到如果用线段树维护,多个块的合并无法维护。
但是对于两个块的合并,是有简单反悔贪心策略的。
这种情况,考虑猫树,我们把一个询问挂在猫树的一个节点上,然后对于这个节点,我们预处理出从中点开始的前后缀的匹配情况,注意到匹配是增量的,所以用主席树直接维护。
为了防止卡空间,需要逐节点处理,这样时间 \(O(n\log n\log V)\),空间 \(O(n\log V)\)。
杂题
P12030
需要注意到的是,最优策略一定是 A 选择最大的 \(a\) 个增加,B 选择最大的 \(b\) 个减少。
因为对于 A,如果不是选择最大的 \(a\) 个,那么一定会让答案变得更小。B 同理。
于是因为策略唯一,我们不妨看做是 A 先把最大的 \(a\) 个加了 \(d\),B 要操作 \(d\) 次。
然后二分答案,考虑计算这些数除了被减了 \(d\) 次不能再减的,其他数的最大值的最小值。
于是二分一个 \(mid\),然后判断一个数 \(v\) 减到 \(mid\) 要多少次。
- 如果要 \(\geq d\) 次,那么要操作 \(d\) 次。
- 否则操作 \(v-mid\) 次。此时这个数还可以继续操作。
如果没有倒欠操作就说明 \(mid\) 合法。然后就可以二分。
二分出最大的 \(mid\),然后如果这个情况下还有剩操作数,那么把多余的操作给还能继续操作的(每个数最多再操作一次)。
CF1149 C
因为直径就是树上最长简单路径,所以我们考虑怎么在括号序上对应一条路径,显然对于路径 \((p,q)\),若 \(p\) 在 \(q\) 前访问,那么在括号序上,就是 \(p\) 的右括号到 \(q\) 的左括号。然后接下来考虑怎么求路径长度。
注意到,我们可以用一个括号子段 \([l,r]\) 的 \(\max_{k\in[l,r)}-sum(l,k)+sum(k+1,r)\) 来表示 \([l,r]\) 的路径长度。
这个东西显然可以搬到线段树上。所以只要在线段树上随便维护几个东西就好了。
CF2143 D2
判定一个序列拆成两个上升子序列有简单的贪心。
然后这个题就是套了个计数。
所以我们把贪心过程的状态放到 dp 状态里。
具体的,我们设 \(f(i,j)\) 表示子序列最后一个位置是 \(i\),另一个子序列最后一个位置是 \(j\) 的方案数。枚举下一个位置是三方的。
然后注意到转移只有两种,树状数组优化做到 \(O(n^2\log n)\)。
CSAcademy-binary_matching
在 dp 的时候维护当前 dp 到文本串第 \(i\) 个位置,用了 \(k\) 个 \(1\),匹配到模式串的第 \(j\) 个位置的最大匹配数,以及最小操作数。
然后转移枚举下一位是 0/1 即可。复杂度 \(O(n^3)\)。要滚动数组。
PS:做这个题只是为了把 vj 上没过的题目清掉(以前做了这个题,然后没过,然后就没有继续调了。?)。
* CF464 E
问题在于怎么进行大整数的加法,以及快速比较。
考虑 \(dis\) 是进行加一个 \(2\) 的次幂转移的,所以二进制位的修改比较有规律。
用主席树维护二进制位,然后比较可以做到 \(O(\log n)\)。
考虑加法怎么实现。我们线段树上二分出低位第一个 \(0\),把其之前的 \(1\) 删去,把 \(0\) 变成 \(1\)。
注意比较节点是否相同需要哈希一下。
复杂度 \(O(n\log^2 n)\)。有点难写。
扫描线
P8512 $^?$
怎么做不出来简单题。。
考虑我们只关心一个数最后的修改,并且查询是全局的。所以我们用树状数组维护第 \(t\) 次操作中被修改的数的值。
然后就是板子了。我们用 ODT 维护连续段,因为只有 assign 操作,所以复杂度是对的。
然后在树状数组上就是单点修改区间查询。
CF997E
不是很困难。
考虑 \([l,r]\) 满足要求当且仅当 \(\max-\min=r-l\)。
已知 \(\max-\min\geq r-l\),所以满足要求当且仅当 \(\max-\min+l\) 是最小的。
所以扫描线,线段树维护全局最小 \(\max-\min+l\) 以及个数,然后全局打:对 \(\max-\min+l\) 最小的位置答案 +1 的标记。
然后线段树下传标记是简单的。
P8868
注意到要求的是子区间的和,所以套路的考虑扫描线历史和。
考虑套路的记录线段树上的信息:\((\sum a,\sum b,\sum ab,\sum_i\sum a_ib_i)\)。
也就是节点的 \(a\) 和,\(b\) 和,\(ab\) 和,\(ab\) 历史和。简记为 \(D=(a,b,ab,s)\)。
考虑怎么打 tag。
首先要有 \(da,db\) 表示 \(a,b\) 的变化量。
然后要更新 \(s\),所以还要记录 \(ca,cb,cab,c\) 表示 \(s\gets c\ s+a\ ca+b\ cb+ab\ cab\)。
于是有 \(T=(da,db,ca,cb,cab,c)\)。
然后 \(T\) 的标记下传会用到子节点的 \(D\)(比如计算 \(ca\) 的时候要用到 \(D_a\)),所以实现时可以把 \(D,T\) 合起来记录。
无脑做法就是维护 \(\begin{bmatrix}a,b,ab,s,len\end{bmatrix}\) 作为信息,然后每次操作(\(a+,b+,s\gets s+ab\))无非就是乘上一些转移矩阵。这样实现常数会比较大。
但是真的无脑。
P7560
仍然是对序列扫描线,对时间轴用线段树维护。
考虑我们要维护的是:假设从 \(l\) 开始操作,当前节点 \([l,r]\) 最后会剩下 \(add\) 人,同时有 \(del\) 个操作没有成功删除人,一共有 \(sdel\) 个删除操作。
然后我们考虑合并信息,有 \((add_1,del_1,sdel_1)+(add_2,del_2+sdel_2)\to(add_1+add_2-\min(add_1,del_2),del_1+del_2-\min(add_1,del_2),sdel_1+sdel_2)\)。
然后操作在扫描线上就是单点操作,这是好维护的。
然后考虑查询。考虑一个位置最后存在,当且仅当左侧(包括自己)的 \(add\) 比右侧(不包括自己)的 \(sdel\) 多。
进一步的,一个位置最后为 \(\text{rk } c\),意味着 \(add_l-sdel_r=c\)。
所以我们需要在线段树上二分找到一个这样的位置。
注意到我们需要维护右侧节点的信息和,所以需要先提取出所有区间,然后枚举区间查询。
复杂度 \(O(n\log n)\)。
dp 优化
* P8290
首先这个值域很难受,考虑类似离散化的操作:我们注意到出现在 \([l_i,r_i]\) 区间只有 \(n\) 个,所以考虑枚举 \(a\in[L,L+k)\)。
首先答案是 \([L,L+k)\) 的答案减去 \([L+1,L+k)\) 的答案。
考虑怎么算。
我们令 \(l_i,r_i\) 是关键点。我们考虑把连续的一段 \(L\) 的答案并在一起算。
注意到,如果 \(L\in[p,q]\),在这段区间内,\([L,L+k)\) 内包含的关键点集合没有变化,那么:每个节点 \(i\) 的贡献可以写成一个不超过 \(2\) 次的多项式 \(f_i(L)\)。
然后答案 \(F(L)\) 是一堆 \(f_i(L)\) 乘起来再加起来,同时乘起来的不超过 \(n\) 个,所以 \(\deg F=O(n)\)。
我们要求出 \(G(p,q)=\sum_{i\in[p,q]}F(i)\),这个的 \(\deg=O(n)\),所以我们考虑拉格朗日插值。考虑求出 \(O(n)\) 个 \(q_i\) 的 \(G(p,q_i)\),这样做一个拉插就可以快速得到 \(G(p,q)\)。
进一步的,我们观察 dp 的过程发现 \(\deg G(p,q)\leq n+2\),所以只要记录 \(n+3\) 个位置就好了。

浙公网安备 33010602011771号