返回顶部
动态线条
动态线条end

26年暑期集训日志

Day 1

开始写 HFC 5,品鉴各种 DP。

CF708C Centroids

换根 DP,对于一个点 \(u\),若其为重心,需满足不存在一个孩子子树大小超过 \(\lfloor\frac n2\rfloor\)。对于存在这样孩子的节点,判断能否消除,最简单的方法就是,找到这个孩子子树内的一棵最大且不超过 \(\lfloor\frac n2\rfloor\) 的子树,让它直接连接到 \(u\),这样子树大小就会减到最小,看最优操作后是否还存在大小超过 \(\lfloor\frac n2\rfloor\) 的孩子子树即可。

可以用可删堆/multiset维护。

CF1009F Dominant Indices

DSU on tree,题目绕了那么多,其实说白了就是找 \(d\) 最大值最靠左的下标,\(d\) 的值可以直接树上启发式合并维护,对于节点 \(u\),将其 \(d\) 值整体右移 \(dep_u\)\(d\) 的值就与子树内所有节点的 \(dep\) 对应了,最后减一下 \(dep_u\) 就可以了。

CF219D Choosing Capital for Treeland

简单换根,建无向边,根据题目给定的有向边赋权,答案为外向树边和,换根求出每个节点的权重即可。

CF543D Road Improvement

计数 DP,对于叶子方案数显然为 \(1\),当条边被选中,其连接的子树都不能被选,该方案不在子树计数范围内,需在转移时加上。转移方程:

\[dp_u=\prod_{v\in son_u}(dp_v+1) \]

然后处理换根时维护前缀/后缀积即可。

最开始写的直接乘逆元,挂了,应该是出现了没有逆元的情况,神秘。

CF1016F Road Projects

诈骗题,细节还巨多,调死我粒!中译中后就是给一棵树,最大化加一条边后 \(1\)\(n\) 的最短路径。

最大化最小?二分答案?滚。

我们称原树上 \(1\)\(n\) 的最短路径为主链,注意到加边会对主链产生影响仅当原树是一只毛毛虫,每个节点还至多只有一条足。如果不满足,就一定存在一种连边,使得其与主链完全无关。那么就是考虑连接两个虫足,使得新产生的“主链”比原主链尽可能的大,如果比它大,那答案就是原主链长度,反之,最小化其差值。由于默认连的边长度为 \(0\),因此在代入边长的时候需做加法,且要和原主链长度取 \(\min\)

Day 2

CF449D Jzzhu and Numbers

SOS DP 啊,题目就是问有多少中选法,使得选中的集合的并刚好是全集。我们对集合计数,随后 SOS DP 求子集和,并出集合 \(s\) 的方案数为 \(2^{dp_s}\),容斥一下求出答案:

\[\sum_{i=0}^S(-1)^{popconut(i)}2^{dp_i} \]

CF939F Cutlet

磕得比较久的一道题啊,一开始坚信 \(O(nk^2)\) 可以过的啊,然后确实写出来了,也是单调队列优化 DP,But 人傻常数大,TLE 了。

简单口糊一下,分两类讨论,在 \([l_i,r_i]\) 内翻两次面,烤一段时间,在两个区间的某一时各翻一次面,烤一段时间。

\(dp_{i,j}\) 表示前 \(i\) 个区间,正面(一开始没烤的那面)烤了 \(j\) 秒的最小翻面数。

不翻:\(dp_{i,j}=dp_{i-1,j}\)

区间内翻两次:\(dp_{i,j}=\displaystyle\min_{1\le k\le j-r_i+l_i}\{dp_{i-1,k}\}+2\)

在两个区间的某一时各翻一次面:\(dp_{i,j}=\displaystyle\min_{1\le s<i}\{\displaystyle\min_{max(j - r_i + l_s , 0)\le k\le min(n , j - l_i + r_s)}dp_{s,k}\}+2\)

单调队列维护即可,时间复杂度 \(O(nk^2)\)

在两个区间的某一时各翻一次面其实没必要枚举在哪个区间,改状态定义:\(dp_{i,j}\) 表示 \(i\) 时刻,正面烤了 \(j\) 秒的最小翻面数。

当前区间可以决定烤了 \(k\) 秒(\(1\le k\le r_i-l_i\))。

翻一次: \(dp_{i,j}=\min\{dp_{i-1,i-j-k}\}+1\)

翻两次:\(dp_{i,j}=\min\{dp_{i-1,j-k}\}+2\)

\(i\) 这一维可以滚动数组滚掉,然后单调队列维护就可以做到 \(O(nk)\) 了。

CF348D Turtles

LGV 引理板子啊,答案就是 \(f(1,2,n-1,m)\times f(2,1,n,m-1)-f(1,2,n,m-1)\times f(2,1,n-1,m)\),其中 \(f(a,b,x,y)\) 表示 \((a,b)\)\((x,y)\) 的路径数,网格路径数计数直接 DP 即可。

CF383E Vowels

依旧 SOS DP,有若干个元素数不超过三的集合,其对其超集的贡献为一,这不直接套个 SOS DP 就完事了,求出的 \(dp_s\) 是不满足条件的单词数,满足的就是 \(n-dp_s\) 呗,求个平方和就可以了。

Day 3

CF512D Fox And Travelling

看起来是图上 DP,实际上数树形 DP。

先模拟一遍,从外忘内扒掉可以扒掉的点,并标记这些点是可以用的,不难发现这些点必定形成一个森林。

随后,森林中的一些树中可能存在一个点连接了一个无用的点,这会占掉一个度,该点必须最后删除,称其为神秘点。显然,一棵树至多存在一个这样的点,若存在两个,其树链不可能连通(其树链都将是无用的点)。

那可以分为两类:有根树和无根树,有根树必定以神秘点为根。

对于有根树,考虑树形 DP,设 \(dp_{u,i}\) 表示子树 \(u\) 按题目要求删 \(i\) 个点的方案数,直接跑树形背包,由乘法原理做乘法,还因为两部分的序列可以任意交错再乘个组合数,转移方程:

\[dp_{u,i}=\sum_{j=1}^i dp_{u,i-j}\times dp_{v,j}\times \binom{i}{j} \]

最后 \(dp_{u,sz_u}=dp_{i,sz_u-1}\),转移不会转移到 \(dp_{u,sz_u}\),而子树的根必须最后删除,不存在新的方案。

\(g_{u,i}\) 表示森林中的树 \(i\)\(j\) 个点的方案数,对于有根树,\(g_{i,j}=dp_{u,j}\),对于无根树,重定义 \(dp_{u,i}\) 表示以 \(u\) 为根,删 \(i\) 个点的方案数,那么 \(g_{i,j}=\frac{\displaystyle\sum_{k\in T_i}dp_{k,j}}{|T_i|-j}\),其中 \(T_i\) 表示树 \(i\) 的点集,每个删除序列在以不同节点为根时被重复计算了 \(|T_i|-j\) 次,需除掉。

然后再做一次背包,设 \(f_i\) 表示从森林中删 \(i\) 个点的方案数,设森林中共有 \(t\) 棵树,转移和树形背包一样:

\[f_i=\sum_{j=1}^tf_{i-k}\times g_{j,k}\times \binom{i}{k} \]

然后做完了。

CF311B Cats Transport

斜率优化 DP,题目可以转换为,在第一象限有若干个点,现在可以画 \(p\)\(y=x+b\) 的直线,\(b\) 自定,每个点的贡献为其到其上方第一条直线的距离。

每个点可以算出刚好过这个点的直线的截距,然后进行排序,随后就变成分段问题了,将序列分成 \(p\) 段,最小总化代价。

维护截距的前缀和 pre,第 \(i\) 个点的截距为 \(b_i\),则一段区间 \((l,r]\) 的贡献 \(w(l,r)\) 为:

\[b_r\times (r-l)-(pre_r-pre_l) \]

\(dp_{i,j}\) 表示前 \(i\) 个点,画 \(j\) 条线的最小总代价,转移方程:

\[dp_{i,j}=\min_{1\le k<j}\{dp_{k,j-1}+w(k,i)\} \]

很典的斜率优化模型,把 \(w(k,i)\) 拆开,\(\min\) 划掉,一番摆弄后可以得出:

\[y=dp_{i,j-1}+pre_i\\k=b_i\\x=i \]

而且 \(x,k\) 都是单调的,可以直接单调队列维护凸包,于是就做完了。

Day 4

加了几道数据结构的题啊,话说这道题不是动态规划题单吗……

CF380E Sereja and Dividing

也是开始写数据结构了啊,N 题 CF 看不到多少分啊,于是写了评分最低的 O(CF 2600*),事实上 N 题更简单。

选定了一个区间,显然从小到大依次进行操作是最优的,那么可以得出计算公式:

\[g([b_l,b_{l+1}\dots b_r],0)=\frac{\frac{\frac{a_l+x}{2}\dots}{2}+a_r}{2} \]

其中 \(a_i\) 为序列第 \(i-(r-l+1)\) 大的数。

给它化简一下就是求:

\[\sum_{i=l}^r \frac{2^{i-l}a_i}{2^{r-l+1}}=\sum_{i=l}^r\frac{a_i}{2^{r-i+1}} \]

考虑 \(b_i\) 对整体的贡献,设 \(f(i,l,r)\) 表示 \([l,r]\) 内比 \(b_i\) 大的数的数量,观察上面的式子可以发现,其对单个区间贡献就是 \(b_i\times 2^{f(i,l,r)}\),那么 \(b_i\) 对答案的贡献就为:

\[\frac{b_i}{2}\sum_{l=1}^i\sum_{r=i}^n\frac{1}{2^{f(i,l,r)}} \]

\(f(i,l,r)\) 是有结合率的,即 \(f(i,l,r)=f(i,l,k)+f(i,k+1,r)\),那么式子可以拆成两个部分:

\[\frac{b_i}{2}(\sum_{l=1}^i\frac{1}{2^{f(i,l,i)}})(\sum_{r=i}^n\frac{1}{2^{f(i,i,r)}}) \]

可以扫描线维护,从大到小枚举 \(b_i\),计算贡献后将其在单点 \([i,i]\) 上乘以 \(\frac 12\),这样可以保证处理 \(b_i\) 时数据结构中维护的信息都比 \(b_i\) 大同时正确计算贡献。

我们需要对区间内的所有前缀积或后缀积求和,可以用区间合并线段树维护,每个区间维护 \(pre,suf,val\) 分别表示当前区间的前缀积的和、后缀积的和、区间积,\(i\) 表示当前区间,\(l,r\) 表示左右孩子区间,易得合并公式:

\[val_i=val_l\times val_r\\pre_i=pre_l+val_l\times pre_r\\suf_i=suf_l\times val_r+suf_r \]

然后就做完了。

CF1146F Leaf Partition

数数题,设 \(dp_{u,0}\) 表示子树 \(u\) 不向上延伸的方案数,\(dp_{u,1}\) 表示向上延伸的方案数。

如果需要向上延伸,那么至少选一个 \(dp_{v,1}\),如果不向上延伸,那么不能存在只有一个 \(dp_{v,1}\) 的情况。

考虑容斥,首先总方案数是 \(\prod (dp_{v,0}+dp_{v,1})\),如果需要向上延伸,要减掉没有 \(dp_{v,1}\) 的情况,也就是减掉 \(\prod dp_{v,0}\);如果不向上延伸,那么需要减掉只有一个 \(dp_{v,1}\) 的情况,也就是 \(\displaystyle\sum dp_{v,1}\prod_{w\ne v}dp_{w,0}\),写成转移方程就是:

\[dp_{u,0}=\prod (dp_{v,0}+dp_{v,1})-\sum dp_{v,1}\prod_{w\ne v}dp_{w,0}\\dp_{u,1}=\prod (dp_{v,0}+dp_{v,1})-\prod dp_{v,0} \]

维护前缀积和后缀积就可以了。

Day 5

但干了事情啊 qwq,一直在磕 N,N 题不简单啊……他们都写的平衡树,我觉得线段树确实可以啊,哎呦……

口糊一下好吧。

CF392D Three Arrays

这是个三维偏序啊,记 \((x_i,y_i,z_i)\) 表示每种数在三个数组中从左往右第一次出现的位置,如果不存在则另设为正无穷。

我们需要找到一个 \((X,Y,Z)\),使得 \(\forall X\ge x_i\lor Y\ge y_i\lor Z\ge z_i\)

枚举 \(X\),转二位偏序,找 \((Y,Z)\)\(\forall Y\ge y_i\lor Z\ge z_i\)

\(x_i>X\) 的点集拍到平面直角坐标系上,对于一个坐标 \((x,y)\),其一定包含所有 \(x_i<=x\land y_i<=y\) 的点,因此若对于一个点 \((x,y)\),若存在 \(x<=x_i\land y<=y_i\),则该点是没有用的。

去掉这些点后,发现剩下的点呈“阶梯型”,可行解就是每个“拐点”的横纵坐标之和,求最小值。

先考虑怎么维护这些拐点,以 \(x\) 为键值,自然升序,维护 \(y\),需保证其降序。本质上是在模拟内部动态插删点的下降单调栈,需要维护前驱后继,插入一个点时需要删除所有小于 \(y\) 的前驱,同时如果 \(y\) 小于其后继,则不应插入。

这拿平衡树很好维护,but 我写的线段树,维护一个 \(sz\) 表示区间内的点数,然后线段树上二分也可以做到找前驱后继。

对于存在正无穷的点,分两类讨论,\(x=inf\) 的点,应当提前处理,对于 \(y=inf\) 的点,我们需要维护 \(R=\displaystyle\max_{y=inf\land z\ne inf}\{z\}\),对于 \(x=inf\),我们需要维护 \(L=\displaystyle\max_{y\ne inf\land z= inf}\{y\}\),这确定了下节。

我们需要找到一个最大的 \(i\),满足 \(y_i\le R\),可以用 map。每个点存一个拐点,那么就用线段树维护区间最小值,不存在的点填 \(inf\)\([L,i]\) 的区间最小值即是答案。

Day 6

模拟赛

模拟赛啊,突然冒出来还是比较慌的啊。

T1 还是差不多半个小时搞定了的,就是单调栈求出以 \(a_i\) 为最小值的最大表示区间,然后 ST 表维护这个区间的最大值,对他们求和取最大值即可。

T2 当然知道是 DP 啊,状态也大致设计出来了,和正解差不多,但是,一直以为状态存不下啊,没想到只用开 \(n\times 30\times 30\times 2\),因为超出 \(10^9\) 直接输出 \(-1\) 就可以了,误判为不可做题跳过了,不过 \(48\) 分还是在最后挣到了的,虽然是乱搞

T3 是数据结构啊,学了吉司机不代表会兔队线段树啊,我也想到了可能需要单侧递归的雏形,因为要求 \(\displaystyle\sum_{i=l}^r qmax(l,i)\)\(\displaystyle\sum_{i=l}^r qmax(i,r)\),其中 \(qmax(l,r)\) 是区间最大值。我知道要根据一个区间的最大值,找到另一个区间的分界点,但让我场推兔队线段树我是没底的,于是我用普通线段树试了很久,一直没结果,这场可以说死在 T3 了。

T4 我看了啊,正解的思路也想到了,但是这题细节蛮多的,代码也犯了糖,也没注意到题目要求加一输出,加上对自己这过于简单的思路产生了质疑,于是被诈骗到了,哎呦……

Day 7

放假,休息 ing……

Day 8

哦,新的一周开始了,把模拟赛的题补完了,严肃学会兔队线段树,然后继续写 CF392D Three Arrays,由于重写也是一种 Debug,于是果断放弃线段树,换纯 STL 维护,轻松 AC。

CF392D Three Arrays

我们从大到小枚举 \(u\) 这一维,所有 \(x_i\le u\) 的点都会被覆盖,剩下的点只需考虑二维,随着 \(u\) 变小,未被 \(u\) 覆盖的点就越多,那么我们就需要用数据结构快速维护:

  1. 在二维平面插入、删除一个点
  2. 找到一个点 \((v,w)\),满足 \(\forall i,((v\ge y_i)\lor (w\ge z_i)\),且 \(v+w\) 最小。

由于需要覆盖所有点,因此有一部分点是没有用的,比如满足 \((y_j\le y_i)\land (z_j\le z_i)\) 的两个点,如果 \((y_i,z_i)\) 被覆盖,那么 \((y_j,z_j)\) 就一定会被覆盖。把这些点去掉之后,我们可以发现对于 \(y_j<y_i\) 的两个点,一定满足 \(z_j>y_i\)

我们对这些点按 \(y_i\) 升序排序,需保证 \(z_i\) 单调下降。用 map 维护,以 \(y_i\) 为键,升序排列,\(z_i\) 为值,手动维护其单调下降。每次插入一个点 \(i\) 后,通过其迭代器枚举前驱后继,若后继 \(j\)\(z_j\ge z_i\),则当前点无效,直接删除;否则枚举前驱 \(k\),删除所有 \(z_k\le z_i\) 的点。每个点至多被插入或删除一次,时间复杂度 \(O(n\log n)\)

那怎么找点 \((v,w)\) 呢?我们目前维护的结构是个二维偏序,有 \(m\) 个点,对于第 \(i\) 个点 \((y_i,z_i)\)\(\forall j\in\{1,2,\dots,i\},y_j\le y_i\),而且 \(\forall k\in\{i,i+2,\dots,m\},z_k\ge z_i\)。因此对于相邻的两点 \((y_i,z_i),(y_{i+1},z_{i+1})\)\(z_{i+1}\) 覆盖了 \(\{i+1,i+2,\dots,m\}\)\(y_i\) 覆盖了 \(\{1,2,\dots,i\}\),两者并起来刚好覆盖了所有点,因此 \((y_i,z_{i+1})\) 是一个可行的点,我们也就只需要求 \(\displaystyle\min_{1\le i<m}\{y_i+z_{i+1}\}\) 即可,可以用可删堆或者 multiset 维护。

然后有一个比较头疼的问题啊,就是被赋值为 \(+\infin\)\(x_i,y_i,z_i\) 怎么办,首先对于 \(x_i=+\infin\) 的点在最开始就没有被覆盖,被插入到二维平面当中。那么对于 \((y_i=+\infin)\land (z_i=+\infin)\) 的点,其只能由 \(u\) 覆盖,不能存在 \(u<x_i\) 的情况,因此可以确定出 \(u\) 的下界,若没有这样的点,下界默认为 \(0\)

如果 \(y_i=+\infin\),且未被 \(u\) 覆盖,就只能由 \(w\) 覆盖,可以确定出 \(w\) 的下界。\(z_i=+\infin\) 的情况同理,这样 \(v\) 的下界也求出来了。因此在 map 维护二维平面的点时,需要删除 \(y_i\)\(z_i\) 低于下界的点。同时,最靠近下界的点同样可以构成可行点,设最靠近下界 \(v\) 的点 \((y_i,z_i)\),此时 \(z_i\) 最大,覆盖了所有点,因此 \((v,z_i)\) 是个可行点。同理,\((y_i,w)\) 也是一个可行点。这些需要单独判断。

完成这些这道题也就差不多做完了。

Day 9 ~ 10

被催促完成《新高一知识点梳理题单》,于是去写那里面的题了,这就是所谓的“怒刷水题找信心”吗,两天写了近 30 道题。。

目前数据结构基础【S1】写完了,树和图【S2】写了一半多,当然,已经写过了的题是没有重写的。

由于太多,只考虑对部分有意义的题进行总结。

P2048 [NOI2010] 超级钢琴

\(i\) 为右端点,左端点 \(j\) 的范围是 \([i-r+1,i-l+1]\),做一个前缀和 \(pre_i=\displaystyle\sum_{j=1}^ia_j\),那么以 \(i\) 为右端点的可以得到的最大贡献就是:\(pre_i-\displaystyle\min_{i-r\le j\le i-l}\{pre_j\}\),用主席树维护区间第 \(k\) 小就可以求出以 \(i\) 为右端点的第 \(k\) 大的贡献,我们用大根堆维护全局最大贡献,每个右端点 \(i\)\(rk_i\) 表示目前可产生第 \(rk_i\) 大的贡献,计算完当前点的贡献后应更新 \(rk_i\gets rk_i+1\) 并重新插入堆中。

那么这道题就解决了,时间复杂度 \(O(n\log n)\)

P3674 小清新人渣的本愿

sto orz

考虑莫队,先思考 \(q=1\) 时该怎么做,由于相同元素可以重复选择,我们只在乎存在性,且值域范围为 \(10^5\),故考虑 bitset 维护。

那么对于询问集合中是否存在两个元素之差为 \(x\),仅当 bitset 中有两个元素的距离为 \(x\),即 (s & (s >> x)).any()

对于询问集合中是否存在两个元素之和为 \(x\),由于值域范围为 \([0,10^5]\),考虑将加法转换为减法。令 \(V=10^5\),若 \(x\le V\),可以得出等式 \(V-b-a=V-x\),其与 \(a+b=x\) 等价,再开一个 bitset 维护 \(V-b\) 即可,计算方法:(s & (t >> (V - x))).any()。若 \(x>V\),改一下式子:\(a-(V-b)=x-V\),就可以用 ((s >> (x - V)) & t).any() 计算了。

对于询问集合中是否存在两个元素之积为 \(x\),直接枚举因子,对于 \(i\mid x\),检查 \(i,\frac{n}{i}\) 是否同时存在于集合之中。

这样差、和的单次查询的复杂度为 \(O(\frac{n}{\omega})\),积的单次查询的复杂度为 \(O(\sqrt{n})\)。由于删增元素的复杂度是 \(O(1)\) 的,\(n,q,V\) 同阶,因此直接跑普通莫队的时间复杂度为 \(O(n\sqrt{n}+\frac{n^2}{\omega})\),可以通过。

P5300 [GXOI/GZOI2019] 与或和

考虑枚举每一位,一个子矩阵按位与后第 \(i\) 位为 \(1\) 仅当矩阵内所有元素第 \(i\) 位全为 \(1\)。于是题目转化为求矩阵内有多少个子矩阵内部元素第 \(i\) 位全为 \(1\),记当前行第 \(i\) 个元素往上有 \(h_i\)\(1\)(包括自己),单调栈求出以 \(h_i\) 为最小值的左端点 \(l\) 与右端点 \(r\),由乘法原理,方案数为 \(h_i\times (i-l+1)\times (r-i+1)\)。对方案数求和,记第 \(i\) 位方案总数为 \(x_i\),对答案的贡献为 \(x_i\times 2^i\)

按位或同理,全 \(0\) 的子矩阵不会产生贡献,拿子矩阵总数减掉即可,全 \(0\) 子矩阵计数方法与 \(1\) 相同,记总数为 \(y_i\),贡献则为 \((\frac{n^2(n+1)^2}{4}-y_i)\times 2^i\)

时间复杂度 \(O(n^2\log V)\)

P1613 跑路

考虑倍增,数据范围很小,建一个三维的邻接矩阵 \(g_{i,j,k}\) 表示 \(i\)\(j\) 间存在长度为 \(2^k\) 的路径,这些路径的边权为 \(1\),预处理的复杂度为 \(O(n^3\log V)\),边数的量级为 \(O(n^2\log V)\)

然后跑最短路即可,时间复杂度 \(O(n^3\log V)\)

P2966 [USACO09DEC] Cow Toll Paths G

对点权排序,然后枚举点权 \(c_l\),对点权不超过 \(c_l\) 的点构成的子图跑 Floyd 开开三维数组存下来,记最大点权 \(c_l\)\(i\)\(j\) 的最短路为 \(g_{i,j,l}\)

之后对于每次查询,回答 \(\displaystyle\min_{max(c_s,c_t)\le i\le n}\{g_{s,t,i}+i\}\)

时间复杂度 \(O(n^4+qn)\)

Day 11

HFC 6 出炉了,交互专场。HFC 5 里的可做题差不多做完了,14/17,于是开始品鉴交互,喜新厌旧嘛。

CF727C Guess the Array

签到题,用 \(n-1\) 次查询相邻元素的和,记 \(b_i=a_i+a_{i+1}\),已知所有的 \(b_i\),反推 \(a_i\)

最后一次查询机会问 \(x=a_1+a_3\),可得 \(a_2=(b_1+b_2-x)\div2,a_1=b_1-a_2\),随后可递推:\(a_i=b_{i-1}-a_{i-1}\),这样就还原了序列。

CF429C Guess the Tree

点错题了,我说怎么交互场会放非交互题,没做出来,想出一个假的做法,由于不是任务范围内的题,被提醒到点错题后果断放弃了,不过还是口糊一下。

先对 \(c_i\) 排序,然后类似迭代加深搜索,求出每个 \(i\) 可能的儿子集合,记为 \(S_i\),需满足 \(c_i-1=\displaystyle\sum_{j\in S_i}c_j\),时间复杂度与迭代加深类似,为 \(O(2^n)\)

随后问题转化为:

从已知 \(n-k\) 个集合族,\(k\) 为叶子节点个数,需从每个集合族中选出恰好一个集合,这些集合互不相交且其它们的并集为全集。

我们把所有集合揉一块,设第 \(i\) 个集合 \(S_i\) 属于第 \(j\) 个集合族,因为共有 \(n\) 个元素,我们往该集合内插入 \(n+j\),那么全集就是包含所有 \(n+n-k\) 个元素。于是就不需要考虑集合族的分组约束了,那么怎么解决这个问题。这是典型的精确覆盖问题,套个舞蹈链板子就可以了。不过毕竟这是 NP-Hard,DLX 的复杂度在随机数据下是没问题的,但构造的矩阵大小是不确定的,面对 CF 的数据估计是寄了,所以果断放弃。

CF2001C Guess The Tree

这才像话嘛。。每次查询会返回树链的中点,类似二分。当中点与左端点重合,说明两点有边相连。于是考虑分治,不断向左右区间递归,直到两点有边相连。如果有边相连,用并查集维护联通块,如果查询的两点属于同一个连通块,说明该树链已经处理完毕,停止递归。这样直接对每个以 \(1\) 为左端点的树链进行操作的操作次数就是 \(O(n\log n)\) 的,也一定可以处理完所有的树边,可以通过。

CF809B Glad to see you!

定义 Nura 点的菜为目标点,每次查询 2 i i+1,如果 \(i\) 右侧有目标点,交互库返回值为 NIE,如果左侧有目标点,交互库返回值为 TAK

于是考虑二分,对于一个区间,若 \([l,mid]\) 内有点,则 \(r\gets mid\),反之 \(l\gets mid+1\)。得到一个目标点 \(x\)。那么剩下那个目标点要么在 \([l,x-1]\),要么在 \([x+1,r]\),对这两个区间都二分一次就可以了。

操作次数为 \(3\log n\),满足要求。

CF811D Vladik and Favorite Game

需要确定上下左右键是否被翻转,注意到如果下一步会走出边界的话会判定为不动,因此可以拿这个来判定。如果 \((1,2)\) 可以走,则可以判断左右键是否被调换;如果 \((2,1)\) 可以走,则可以判断上下键是否被调换。确定好之后需要判断另外两个键是否翻转,由于题目保证一定有道路抵达终点,说明一直往右走一定会有一条可以往下的道路或者一直往下走一定会有一条可以往右走的道路,否则直接往右/下就可以到终点。于是就确定了上下左右的实际键位,做反方向回到起点,跑 DFS 找到一条通往终点的路走过去就可以了。

CF1286C1 Madhouse (Easy version)

注意到 \([1,n]\)\([2,n]\) 的所有子串集合的补集恰好为字符串的前缀集合,用两次查询次数查询这两个区间,交互库返回的子串为 \(\frac{(n+1)n}{2}+\frac{(n-1)n}{2}=n^2\),都在题目限制范围内。

于是做一下容斥即可得出前缀集合,第 \(i\) 个字符可以由 \([1,i]-[1,i-1]\) 得出,这道题就做完了。

Day 12

上午补记录 ing……

CF835E The penguin's game

我怎么不会二进制分组。。

依次询问第 \(i\) 位为 \(1\) 的下标集,集合内仅有一个目标点时,交互库的返回值为 \(y\)\(x\oplus y\),于是可以得到目标位置的异或和,然后对任意一个仅有一个目标点的下标集二分,确定其中一个位置的下标,然后根据异或和得到另一个位置的下标。

好了我会二进制分组了。

CF1174F Ehab and the Big Finale

思路是这天想的,题是第二天 AC 的。

先对树重链剖分,然后看目标点是否在重儿子子树中。第一次查询问根到目标点的距离 \(len\)

然后令 \(u=1\),沿着重链走 \(len\) 步得到的点 \(v\),这里可以暴力也可以预处理倍增数组。然后查询 \(v\) 到目标点的距离 \(w\),如果 \(w\) 小于等于重链长度,那么其一定在重儿子子树中,反之问从 \(u\) 到目标点的路径上的第二个节点,若其为重儿子,其也一定在重儿子子树内,反之不在。

这里需要注意一点,重链长度可能小于 \(len\),因此如果小于 \(len\),就应令 \(v\) 等于重链链尾并记录重链长度与 \(len\) 的差值 \(c\)

若目标点在重儿子子树内,则可计算出目标点到重链的距离为 \(\frac{w+c}{2}\),看根据这个求出 \(v\) 与目标点的 \(LCA\),其到 \(u\) 的距离为 \(len-\frac{w+c}{2}\),如果 \(LCA\) 是目标点,直接输出答案,反之其不在 \(LCA\) 的重儿子子树内,询问其到目标点的路径上的第二个节点,递归到轻儿子即可。

若不在重儿子子树内,则递归到轻儿子,更新 \(len\),重复操作直至 \(len=0\)

由树链剖分的性质,询问次数为 \(O(\log n)\),而且跑不满,可以轻松通过。

Day 13

模拟赛

从哪冒出来的模拟赛???

状态不好啊,近几天一直拉肚子,然后昨天晚上还给骨骼侧弯的右脚套了一个矫正器,骨肉割裂的痛感让人睡不好觉,于是整个人都是昏沉的。

进场,开 T1,注意到会产生影响的有且仅有相同数量大于 \(1\) 的二元组,把他们提取出来。用 vector 存下第 \(i\) 个元素分别属于哪几个二元组,一个元素可同时属于多个,也可能一个也不属于。贪心地找属于二元组数目最少的元素,除了该元素属于的二元组,其他二元组都会被消掉,\(O(n)\) 暴力更新即可。接下来只需继续处理未被消掉的二元组,重复操作,直到所有二元组被消掉。显然,操作数是 \(O(1)\) 级别的,因此时间复杂度为 \(O(n)\),可以通过。

思路是很快就想出来了的,但是实现花了近一个小时,不知道为什么。

看 T2,花了十来分钟,推出了组合式子。题目就是前缀是定死的,只在乎后缀集合,后缀集合内的元素是任意排的,但由于有重复元素,相邻元素交换是不算新方案的,由除法原理,方案数为 \(\frac{k!}{\prod c_i!}\),其中 \(k\) 表示后缀可重集大小,\(c_i\) 表示元素 \(i\) 的个数。答案也就是对每个后缀集合求和。维护一个整体的阶乘逆元积 \(iv\),然后拿 \(k!\) 乘,往后枚举的时候乘个阶乘就算是删除了。

然后啊,你要满足 \(a_i<b_i\) 这个约束啊,直接枚举第 \(i\) 位填什么复杂度是 \(O(nm)\),考虑把 \(a_i\)\(iv\) 中移除的贡献就是 \(\frac{c_{a_i}!}{(c_{a_i}-1)!}\),维护前缀和,单点修,BIT 就搞定了,时间复杂度 \(O(n\log m)\)

但是场上没 \(A\),停在了 \(O(nm)\),为什么呢?不是要离散化吗?算 \(iv\) 的代码写在了离散化前面,导致每个 \(a_i\) 都多算了 \(c_{a_i}-1\) 次,然后数据又水,过了很多个点,小数据全过,搞得我以为是下面维护删点错了,调没调出来,磕了一个小时发现时间不够直接跳过了……

T3,打表找规律题啊,不过认为不太可做,相较于 T4,我认为应在 T4 投入更多时间,于是采用暴力预处理和打表分块拿了 60pts 果断跳过,没有去仔细找规律 qwq。

T4,DP 啊,对于 \((x,y)\),需要求出从 \((1,1)\)\((x,y)\) 的最短路上最多能经过多少个与 \(a_{x,y}\) 相同的元素,以及从 \((x,y)\)\((n,n)\) 的最短路上最多能经过多少个与 \(a_{x,y}\) 相同的元素,答案就是统计两者之和的出现次数。

转移方程很好想嘛:

\[dp_{x,y}=\max_{1\le i\le x,1\le j\le y,a_{i,j}=a_{x,y}}\{dp_{i,j}\}+1\\dp2_{x,y}=\max_{x\le i\le n,y\le j\le n,a_{i,j}=a_{x,y}}\{dp2_{i,j}\}+1 \]

就是二维偏序嘛,依次处理每种 \(a_{x,y}\),用 vector 存下位置,然后以 \(x\) 为第一关键字、\(y\) 为第二关键字排序,然后直接枚举时就消掉了 \(1\le i\le x\) 的约束,只需考虑 \(1\le j\le y\),就是单点修,查前缀最大值嘛,由于 \(a_{x,y}\)\(O(n^2)\) 种,不允许 \(O(n)\) 预处理与清空,于是考虑动态开点线段树,就可以做到 \(O(1)\) 预处理与清空了。\(dp2\) 同理,就是反过来求一遍就好了。

但时间不多了啊,最终是没能写出来的,只挣到 70 分。

反思就是,太过于保守了啊,每次都是按照部分分一点一点往下写的,如果其中一环卡住就会出现时间不足、无法继续往下写的情况,有时需要激进点,对于能力范围内的题还是直接敲正解吧。

Day 14

放假,休息 ing……

Day 15

NOI 模拟(全省统考)

沿用省队集训的 OJ 啊,账号都没变,题目貌似是省集时模拟赛没选上的题,也就是没过审的题。题肯定是好题,但没过审的题一般都是不友好的题。

看 T1,开始觉得比较友好啊,就是交换相邻元素看至少多少步可以构成两个拼在一起的上升序列。

于是快速想出一个伪的做法,维护前缀集合的逆序对数和后缀集合的逆序对数,然后枚举这俩上升序列的分界点,答案就是前缀集合的逆序对数加上后缀集合的逆序对数,然后再多考虑一下分界点的两个元素交换后逆序对数就可以了。

但是过不了大样例,仔细想想发现伪了,因为两个上升序列并不互相独立,那就不是很好做了,跳过。

看 T2,神秘概率题,手推二项式分布,然后得出了下面这一坨式子。

\[\sum_{i=0}^n\binom{n}{i}x^i(1-x)^{n-i}\sum_{j=0}^{\min(m,i-k-1)}\binom{m}{j}y^j(1-y)^{m-j} \]

这啥啊

意思就是进行 \(n+m\) 轮对局,游戏一先手胜利的概率是 \(x\),进行 \(n\) 局;游戏二先手胜利的概率是 \(y\),进行 \(m\) 局,问先手胜利局数至少比后手胜利局数多 \(k\) 的概率。

推出来后发现 \(n\)\(m\) 在环上期大小是无限递增的,即使有规律但这式子就不一定对也不好算。也许可以用斯特林数把普通幂转下降幂?貌似不行,于是 \(5\) 分签到跑路(然后这 \(5\) 分还挂了,神人题面描述)。

然后发现 T3 才是最好拿分的,于是会 \(O(1)\) 查询,\(O(n^2)\) 修改,再拿 BIT 糊弄了一个特殊性质,获得 \(24pts\)

最终:\(15+0+24=39\)\(rk3\)

T1 居然有分,感动哭了,但仔细一想如果打个暴力就有 \(25\) 分,状压一下就可以。然后 T3 应该要会 \(O(n)\) 查询,\(O(n)\) 修改的部分分,这样就有 \(48\) 分。

所以本应 \(25+5+48=78\)怎么还是这么低)。

Day 16

CF1364E X-OR

随机化,蠕动了一天,最后无奈赫的题解 qwq

先讲下自己的思路吧,虽然是错的。

首先随机一个位置 \(c\),对于其他位置 \(i\),花 \(n-1\) 次询问求出 \(a_i=c\lor p_i\),其中 \(\lor\) 是按位或。

随后得出 \(\displaystyle p_c=\bigwedge_{i\ne c}a_i\),其中 \(\land\) 是按位与。

我们需要求出两个数 \(x,y\) 满足 \((p_x\lor p_y)\land p_c = 0\),此时 \(p_x=a_x\oplus p_c,p_y=a_y\oplus p_c\),其中 \(\oplus\) 是按位异或。

然后对于 \(a_i=p_c\),说明 \(p_i\land p_c=p_i\),显然 \(p_x\land p_i = 0\),因此 \(p_i=(p_i\lor p_x)\oplus p_x\),然后就可以找到 \(p_j=0\) 时的 \(j\),由于 \(\forall i,p_i=p_i\lor 0\),于是可以推出所有位置的值。

但由于 \(x,y\) 不好求,于是做法假了。

正解做法也是找 \(0\),但是实实在在的随机化算法。一个结论:若 \(p_c\lor p_i=p_c\),说明 \(p_i\)\(p_c\) 的子集,此时令 \(c\gets i\),重复操作,最终一定可以找到 \(0\) 所在的位置。

证明很简单,集合大小是递减的,且 \(p_c\lor 0=p_c\) 恒成立,问题在于怎么求 \(p_c\) 的实际值。我的做法是:\(\displaystyle p_c=\bigwedge_{i\ne c}p_i\lor p_c\),这么做是 \(O(n)\) 的。

其实我们可以随机 \(k\) 个元素,构成的集合为 \(S\),然后得出 \(\displaystyle p_c=\bigwedge_{i\in S}p_i\lor p_c\),验证其正确性,我们逐位考虑,对于第 \(i\) 位,若其为 \(0\),如果想使答案错误,就需要 \(S\) 内所有元素的第 \(i\) 位全部为 \(1\),这样的概率是 \(2^k\),因此 \(k\) 取到 \(20\) 时就基本不会出错了。

求出 \(0\) 的位置就可以求出其他所有位置的值了,然后就做完了。

Day 17

今天怎么没模拟赛?

CF2106G2 Baudelaire (hard version)

若已知根,那么就可以通过查询每个点到根的树链和 \(pre_u\),不难得出点 \(u\) 的点权为 \(pre_u-pre_{fa_u}\),这里会消耗 \(O(n)\) 次查询。

于是要要考虑怎样在 \(200\) 操作内找到根。首先有一点显然,一个点 \(u\) 若有父亲,那其一定不为根,其子树内的所有元素也一定不为根。于是考虑重心,由重心的性质,每次至少淘汰一半的点,从大到小枚举,只需枚举 \(O(\log n)\) 个重心。

然后就是对于每个重心,需要快速找到其父节点,注意到节点 \(u\) 的值翻转后,对父亲的 \(pre_{fa_u}\) 没有影响,对后代节点会产生 \(\pm 2\) 的贡献,翻转后后代节点 \(i\) 的贡献为 \(f(i)\rq=f(i)\pm 2\),因此对应儿子集合 \(S_u\),二分其子集 \(C_u\)\(C_u\) 内没有父节点仅当:

\[\frac{|\displaystyle\sum_{i\in C_u}f(i)-\sum_{i\in C_u}f(i)\rq|}{2}=|C_u| \]

反之则有父节点。于是对于每个重心,可以用 \(O(\log n)\) 次查询确定其父节点,然后递归到父节点所在的子树继续找重心,重复此操作就可以找到根节点,总操作数 \(O(\log^2 n)\),而且常数较小。

具体实现就是套了个点分治板子,然后里面再套个二分,总操作数为 \(O(n+log^2 n)\),可以通过。

P4408 [NOI2003] 逃学的小孩 / 数据生成器

题目可以转换为:在树中找到一个点 \(u\),满足树上存在三点 \(v_1,v_2,v_3\),其到 \(u\) 的距离从小到大排序后分别为 \(w_1,w_2,w_3\),使得 \(w_1+2w_2+w_3\) 最大,求这个最大值。

考虑 DP,设 \(dp_{u,i}\) 表示子树 \(u\) 内,\(v\)\(u\) 的距离长度第 \(i\) 长的长度,注意只需找前三长。

先跑一轮 DFS 求出所有的 DP 值,然后再跑一遍 DFS,可以换根或者维护父方的最长链,取前三长者计算答案。

Day 18

CF1990E1 Catch the Mole(Easy Version)

要是鼹鼠不动就好做了,但是鼹鼠会动,讨厌。

涉及到子树查询,自然想到重剖,然后分类讨论。

先做一个简化,若目前鼹鼠在子树 \(u\) 内,\(Q_u=1\),否则 \(Q_u=0\)

首先显然的一点,若 \(Q_u=1\),询问了一个 \(v\) 使得 \(Q_v=0\) 时,鼹鼠会往上走一步,若此时 \(Q_u=0\),说明鼹鼠已经不在子树 \(u\) 内且恰好从 \(u\) 往上走了两步。因此只要出现了这种情况,就可以直接确定鼹鼠的位置。

若鼹鼠在重儿子子树内,需快速定位重链上的一点,满足鼹鼠在子树内,且离鼹鼠最近。不难想到二分,将重链中的节点提取出来,按深度从小到大排序。设 \(res\) 表示当前子树内包含鼹鼠的最优节点,已知重儿子子树一定包含鼹鼠,故初始令 \(res\) 的值为重儿子。对于第 \(mid\) 个节点,若 \(Q_{mid}=1\),则鼹鼠不会动,更新 \(res\) 并缩小左边界即可;反之 \(Q_{mid}=0\),鼹鼠就会往上动了,若 \(Q_{res}=0\),则可直接得出鼹鼠的位置,否则正常缩小右边界,继续二分即可。

然后考虑鼹鼠不在重儿子子树内的情况,说明询问重儿子时交互库返回了 \(0\),此时鼹鼠会往上跳一步,若 \(Q_u=0\),则同样可以确定鼹鼠的位置。

考虑按孩子子树高度从大到小枚举每个轻儿子,若 \(Q_v=1\),则直接递归到该子树继续寻找;反之就是 \(Q_v=0\) 的情况,鼹鼠会往上跳,判断 \(Q_u=0\),即鼹鼠位置是否可以直接确定。

在枚举轻儿子的过程中,由于按子树高度从大到小排序,因此剩余子树的最大高度是不增的,在处理过程中如果最大高度足够小,可以通过让鼹鼠跳到 \(u\) 来确定鼹鼠的位置,即重复询问重儿子使得鼹鼠往上跳,直到 \(Q_u=0\),就可以得出鼹鼠的位置了。当然,如果 \(u=1\),即 \(u\) 为根,可直接用完所有的操作次数,让鼹鼠一直往上跳,最后鼹鼠的位置就为 \(1\) 了。

最后,若 \(u\) 为叶子,则鼹鼠位置就为 \(u\)

然后发现这么做甚至可以在 \(160\) 次询问内找到鼹鼠,可以通过本题的 Hard Version。为什么呢,由重链剖分的性质,递归处理的次数为 \(O(\log n)\),重儿子需要在重链上二分,二分的操作数是 \(O(\log n)\),而且常数较小。那轻儿子的操作数呢?考虑轻儿子最多会花多少次操作才可以找到鼹鼠在哪个轻儿子子树中,如果最大高度足够小,就可以通过让鼹鼠往上跳来确定鼹鼠的位置,那么如果要规避这种情况,就需要尽可能多的轻儿子子树高度超过剩余操作数 \(w\),这样的轻儿子的量级为 \(O(\frac{n}{w})\),由于子树大小在递归后会不断减半,加上 \(\frac{n}{w}\) 本来就很小,这样的量级可以看做 \(O(\log n)\)

综上所述,总操作数为 \(O(\log^2 n)\),而且常数较小,足以通过本题。

posted @ 2026-07-08 08:50  Kei酱  阅读(44)  评论(0)    收藏  举报