Cry_For_theMoon  

一些以前遗留的题如果想起来了也写进这吧,总归要整理的。

ARC171

B. Chmax

600 分的 ARC B 确实有点难度。

先理清操作的实际含义:建出 \(i\rightarrow p_i\) 的置换环结构,在置换环上从点 \(i\) 开始走,当下一个点的编号大于自身的时候就走,否则就停下来。然后 \(B_i\) 就代表最后停留的位置。因此比较平凡地,有 \(B_i\ge i\),且 \(B_{B_i}=B_i\)

对一个确定的 \(P\) 而言,所有满足 \(B_i=x\) 的人应该维护同一置换环上连续的一段,且这些人应该按照编号升序排布。对于一个 \(x\),我们把所有 \(B_i=x\) 的称为一类。同一类的数一定紧密相连,升序排列。因此可以看成一个整体。令 \(S(x)\) 是所有 \(B_i=x\) 的位置里最小的 \(i\)

一个置换环由若干个等价类拼接而成,等价类的拼接需要满足:若在置换环上,等价类 \(x\) 的下一步进入了等价类 \(y\),那么一定有 \(x\gt S(y)\),否则当一个数走到 \(x\) 的时候它会选择继续向下走,而不是停留在 \(x\)

因此,我们可以把对排列计数看成为每个等价类选后继。相当于把等价类看成点,然后对它们的合法置换群计数。

\(i:1\rightarrow n\) 扫描,构建一个序列。如果存在某个 \(S(x)=i\),就加入左括号。如果 \(B_i=i\),就加入右括号(同时满足两个条件的话先加入左括号)。然后计数的内容变为了:对每个右括号选一个位于之前的左括号匹配的方案数。

因此直接顺序扫描序列,维护一个变量表示当前前缀有多少未匹配的左括号。然后 \(O(n)\) 解决。

代码

C. Swap on Tree

感觉难度很高,vp 的时候写了一个 dp 的做法,发现有纰漏了以后往错误的方向继续想了。第二天来修了一下系数就通过了。究其原因还是想的不够明白。

忽略复杂的交换过程,直接尝试刻画一个最终局面合法的条件。

对于一个点 \(u\) 子树,我们最多只有一次机会,交换 \((u,fa_u)\) 来沟通子树 \(u\) 与外界,因此一个必要条件是,每个子树内最多只有一个外来点。且这个外来点是谁都无所谓,因为它一定是从 \(fa_u\) 换进来的。

这启发我们设 \(f(u,0/1)\) 表示 \(u\) 子树内的最终局面已经确定,是否有一个数会被换出去的方案数。

转移时合并子树信息:令 \(g(c)\) 表示有 \(c\) 个儿子会有数需要换出去的方案数。

这里需要一些分类讨论:

  • \(u\) 在原地不动,仅在 \(c=0\) 的时候成立,因为子树之间显然无法进行任何交换了。以 \(1\) 的系数贡献至 \(f(0)\)
  • 否则点 \(u\) 一定经过了交换。考虑 \(u\) 子树内部有点换了出去和没有换出去两种情况:
    • 若没有点换出去,则考虑交换只发生在 \(u\) 和儿子处。共有 \(c\) 次交换,调换任意两次交换的先后顺序都会导出不同的结果,因此以 \(c!\) 的系数贡献至 \(f(0)\),前提是 \(c\ge 1\)
    • 若有点换出去,则考虑实质上就有 \(c+1\) 次交换,较之上一种情况,多出了 \((u,fa_u)\) 边的交换。同样调换任意两次交换的先后顺序都会导致不同的结果,因此有 \(c!\) 的系数贡献至 \(f(1)\),这里不要求 \(c\ge 1\)

然后就得到了一个 \(O(n^2)\) 的做法。

代码

E. Rookhopper's Tour

D 题太简单了不写。这个 E 题很有意思。

先进行一些基本的观察分析:\(m\) 肯定是偶数。我们的路线一定是竖直、水平交替。最后一次消除的白棋一定和 \((A,B)\) 相邻。还有呢?

随便按照题面的过程走几步:发现第一步会有竖直水平两种选择,接下来永远只有一种走法可走(甚至可能会无法继续走)。如果路线唯一就好了,因为可以把合法局面计数变成对合法路线计数。事实上任何合法局面也恰好仅有一条合法路线。

如果有两条合法路线,那其实应该是同一条的正反走法。这就要求路线的每一步都可逆。可逆当且仅当黑棋当前位置和下一次移除的白棋相邻。而我们又要求第一次和最后一次移除的白棋都与 \((A,B)\) 相邻,容易发现此时就一定不存在一条可逆的回路。

以下默认第一步竖直走,第一步水平走只需要交换 \((A,B)\) 后计算即可。

接下来就没想明白了,为什么呢?可能是往 dp 想了,然后发现走一步是影响的连续的两行/列,非常难记录进状态里,就 gg 了。

还是考虑忽略过程看最终路线的样貌,这是一个很 ARC 的想法。。

第一步一定是 \((A,B)\) 出发,移除某个 \((x,B)\) 然后走到 \((x-1/x+1,B)\),然后第二步再水平出发。相当于第一步宣告了有连续的两行有白棋,第二部宣告了连续的两列有白棋。整个路径就是一个 \(\frac{M}{2}\) 次宣告行,\(\frac{M}{2}\) 次宣告列的过程。最后恰好有 \(M\)\(M\) 列被宣告了有白棋,非常完美。

然后来刻画路径从 \((A,B)\) 开始的条件:相当于第一步宣告的行里不能有第 \(A\) 行,第二部宣告的列里不能有第\(B\) 列。

再刻画路径从 \((A,B)\) 结束的条件:相当于第 \(M-1\) 步宣告的行里必须有第 \(A\) 行,第 \(M\) 步宣告的列里必须有第 \(B\) 列。

因此容易看出有解的另一必要条件是 \(M\ge 4\)。考虑枚举第 \(M-1\) 和第 \(M\) 步的具体行动,共有 \(4\) 种可能。

确定了最后两步以后,再来确定前面 \(M-2\) 步的方案数。行、列的选择是完全独立的,只用考虑如何算剩余 \(\frac{M}{2}-1\) 步行操作的方案数:第 \(M-1\) 步选出的两行把棋盘划分成了上下两部分,我们枚举在上半部选择了 \(c\) 个连续的两行,则就应该在下半部分选 \(\frac{M}{2}-1\) 个连续的两行,在 \(n\) 行里选出 \(m\) 个连续的两行的方案数容易使用插板法 \(O(1)\) 算出,再用 \(\binom{\frac{M}{2}-1}{c}\) 作为系数结合上下半部。

还遗漏了一个条件:比如设第 \(M-1\) 步宣告了 \((A-1,A)\) 两行,则实际过程中应该是我们从上半部出发,移除了一个 \((A-1,B)\) 的白棋来到 \((A,B)\)。因此此时要求 \(c\gt 0\) 且d第 \(M-3\) 步的宣告位于上半部。对于宣告 \((A,A+1)\) 两行的情况同理,我们要求第 \(M-3\) 步的宣告位于下半部。因此需要略微修改合并时的组合数系数,以及微调 \(c\) 的上下界。

此时我们就在 \(O(n)\) 的时间内解决了。

代码

F. Both Reversible

这个题更趣味,一定要学会了来补。

现在大致学会了,还有一步的证明没有理解。感觉非常需要观察力,猜想力和踏实的推式子能力,非常牛的题。

可以写一个搜索来观察 \(n\) 较小的时候解的情形:发现 \(n\) 为奇数的时候一定是有非平凡回文循环节,但偶数的时候会有一些例外,比如:$$\text{ababbaab}$$,或者第一个样例中的 $$\text{abab}$$ 也是。

一般而言这种题目里都会需要倒来倒去,充分利用 \(A+rev(B)\)\(rev(A)+B\) 的性质。

比如说,不妨设 \(|A|\le |B|\),那你发现 \(A\) 必定是 \(B\) 的前缀。因此令 \(B=A+C\)。则 \(A+rev(B)\) 回文等价于 \(A+rev(C)+rev(A)\) 回文等价于 \(rev(C)\) 回文等价于 \(C\) 回文。然后 \(rev(A)+B\) 回文等价于 \(rev(A)+A+C\) 回文。因此令 \(X:=rev(A)+A,Y:=C\),则相当于我们要求:\(X\) 回文,\(Y\) 回文,\(X+Y\) 也回文。所有合法的 \((X,Y)\)(注意到 \(|X|\) 必须是偶数)。

此时有比较经典的结论是:一定存在一个本原回文串 \(T\) 使得 \(X=T^n\)\(Y=T^m\)(本原指的是说 \(T\) 不存在更小的回文整周期)。

\(n\) 是偶数的时候就有,\(A=T^{\frac{n}{2}},B=T^{\frac{n}{2}+m}\)。当 \(n\) 是奇数的时候,首先 \(T\) 的长度必须是偶数,假设其形如 \(rev(S)+S\) 的形式,那么 \(A=S+T^{\frac{n-1}{2}},B=S+T^{\frac{n-1}{2}+m}\)

对于 \(|A|\gt |B|\) 的情况也同理。最后我们得出,合法的拆分方式一定形如下面两种:

  • \(A=T^x,B=T^y\),其中 \(x,y\) 为正整数。
  • \(T=rev(X)+X,A=X+T^{x},B=X+T^{y}\),其中 \(x,y\) 为非负整数且 \(X\neq rev(X)\)

到这里也能和最开始的小观察吻合:下面一种 case 得到的串长度始终为偶数,所以 \(n\) 为奇数只有上面一种 case 的还存在。

并且注意到:第一种方式得到的串一定自身回文,第二种方式得到的串一定自身不回文。所以首先两种拆分方式独立。

第一种情况非常容易,枚举一下 \(|T|\) 然后做一个倍数容斥即可。

第二种情况的话,首先一个很重要的事情是,任何一个非回文串至多会有一种合法拆分方式。证明尝试理解失败了。可以打表猜测?

然后对串计数就变成了对拆分方式计数:自然的想法是枚举 \(x=|X|\) 然后显然 \(x\mid n\)\(\frac{n}{x}\) 是偶数。然后我们把原串划分成了 \(\frac{n}{x}\) 段,枚举一个奇数编号的段 \(i\),然后就是说 \(A\) 由前 \(i\) 段组成,\(B\) 由后面的段组成。那么每一段到底取 \(X\) 还是 \(rev(X)\) 也已经知晓。此时我们合并等价类后得到了一个 \(O(x)\) 的数组 \(b\)\(b\) 的每个位置要么被钦定,要么是问号。然后需要计数的是:有多少种可能的 \(b\) 使得 \(rev(b)+b\) 是本原的。这部分的复杂度是 \(O(d(n)\times \frac{n}{x}\times x)\),我的实现比较烂,多带了 \(\Sigma\)。所以是 \(O(nd(n)\Sigma )\)

然后接下来本原这个事情考虑就容斥掉:枚举一个 \(2x\) 的约数 \(z\) 满足 \(\mu(\frac{2x}{z})\) 不为 \(0\) 即可。然后容易 \(O(x)\) 计算方案数。

这样的话,这部分的复杂度是 \(O(d(n)\times\frac{n}{x}\times d(x)\times x)\) 也就是有一个理论上界是 \(O(nd(n)^2)\) 的,但显然跑不满。然后可以通过此题。

代码


IOI2023

在冬令营的时候听了国家队的讲课,觉得这一场的题目相比 2022 更为贴近 CNOI,还是很有做的必要的。

Day1 T1. 封锁时刻

这个题还在我的能力范畴内。

\(f_u\)\(u\) 到两个起点较近的距离,\(g_u\) 是较远的。容易想到抽象成以下问题:

  • 对于每个点 \(u\),付出 \(f_u\) 的代价可以获得 \(1\) 的收益,付出 \(g_u\) 的代价可以获得 \(2\) 的收益(\(f\le g\))。要求总代价 \(\le k\) 且收益最大。

理由是这是一个经典的问题。然而这个转化有误:由于我们把点独立开了,就先需要证明我们若选了一个决策,则在原图上其前驱决策也被选了。

前驱决策的定义:比如说,我们若把 \(u\) 调成了 \(Y\) 可达,则考虑 \(Y\rightarrow u\) 的路径上的倒数第二个点 \(v\),其一定也被调为了至少 \(Y\) 可达。不难发现前驱决策的代价一定不大于当前决策。

讨论后会发现唯一的问题出现在,\(X\)\(Y\) 的路径上,中点的两侧。设两个点为 \(p,q\)。则 \(g_p\) 的前驱决策是 \(f_q\)

对于其余的情况,\(g_u\) 的前驱都是 \(g_v\)。所以如果你没有选 \(g_v\) 一定不会选 \(g_u\)。但是现在,我们可能会出现不选 \(f_q\) 就去选 \(g_p\) 的情况,毕竟前者收益仅为 \(1\) 而后者收益为 \(2\)

当意识到这个仅存的特殊情况后,补丁就很容易打了:当我们让某个点同时能被 \(X,Y\) 可达时,两点之间路径上的点一定都会先设置成至少为 \(f\)。所以我们讨论一下是否有人选择了 \(2\) 的收益。若没有,则等价于把 \(g\) 全设为 \(\infty\)。若没有,则等价于对于每个 \(X,Y\) 之间的点 \(u\),都先付出 \(f\) 的代价获得 \(1\) 的收益,然后令 \(f:=g-f\)\(g:=\infty\) 即可。

现在再来回顾”经典问题“是如何解决的。常规做法是反悔贪心 / 模拟费用流。还有一个更简单的做法:

\(f\le g-f\) 的时候,我们可以把一个点拆成两个点,选了收益为 \(1\),代价分别为 \(f/g-f\)

\(f\gt g-f\) 的时候,我们把一个点设置成了 \(f_u\),那再花费更小的额外代价就能设置成 \(g_u\) 多获得 \(1\) 的代价。换言之就是,只有最多一个 \(u\) 会选择成 \(f_u\),剩下的要么不选,要么选成 \(g_u\)。我们枚举这部分里有 \(k\) 个选了 \(g_u\),则一定选的是 \(g\) 最小的 \(k\) 个。剩下的点和第一类混在一起按照 \(f\) 排序,然后贪心地能选就选即可。上述过程使用线段树维护就容易做到 \(O(n\log n)\)

代码

Day1 T2. 最长路程

这个题就不是我的范畴内了,只能说非常厉害。

由于这个图非常稠密,任意三点之间都有一条边。先猜测其是连通的,发现只有一个反例是两个完全图的情况。再猜测对于一个连通块,一定能找到一条哈密顿路。

考虑使用增量法构造。同时维护两条路径,分别记作 \(u\rightarrow v\)\(p\rightarrow q\),然后考虑现在加入一个点 \(x\)

\((v,q,x)\) 三者之间必定有一条边。如果是 \((v,x)\)\((q,x)\) 就直接把 \(x\) 接上。否则 \((v,q)\) 有边,本来的两条路径合并成一条,新的第二条路径为 \(x\rightarrow x\)

最后把所有点划分成了两条路径,如果两条路径直接无交那就是两个连通块的情况,输出较长路径即可。否则我们再将其拼接成一条哈密顿路。

假设 \(u\neq v\)。然后 \((u,v,p)\) 三者间必定有一条边。若这条边是 \((u,p)\)\((v,p)\) 直接就能拼接。否则说明第一条路径其实是回路。

对第二条路径做同样的事情,然后现在我们得到了两个回路而不是路径(认为大小为 \(1\) 的路径也算回路)。

两个回路直接的拼接非常容易,我们只需要求出一对具体的 \((x,y)\) 满足 \(x\in u\rightarrow v\)\(y\in p\rightarrow q\)\((x,y)\) 有连边即可。这可以两次二分出。然后我们得到了一个 \(2(n+\log)\) 的做法。还无法通过。

考虑 \(2n\) 已经大于 \(400\) 了,必须优化这里。尝试用 \(3\) 次操作加入两个点。事实上也是可以做到的。假设我们目前尝试加入 \((x,y)\)

  • \((x,y)\) 有连边。

    此时考虑 \((v,q,x)\) 三者,若存在 \((v,x)\)\((q,x)\) 的边直接拼接即可。否则存在 \((v,q)\) 的边,依旧是原先的两条路径合并为一条,新的第二条路径为 \(x\rightarrow y\)

  • \((x,y)\) 无连边。

    此时 \((x,v)/(y,v)\) 必定存在一条。不妨假设 \((x,v)\) 存在。然后 \((x,q)/(y,q)\) 必定存在一条。哪种情况都能合并成两条路径。

然后我们在 \(1.5n+\log\) 的次数内解决了本题。

代码

Day1 T3. 足球场

这个题的思维难度没有上一题高,但是在实现上需要精细思考。

先刻画一下一个连通块合法的充要条件:

首先是,若两个同行的点被选中,他们之间不能有未选中的点。同列的两个点同理。因此这是个凸包状物的结构。

思考一下发现并不充分:如果是一个两行的图形,并且区间的位置关系是相交但不包含,比如第一行位于 \([1,3]\) 第二行位于 \([3,5]\)。同样无法在两步内从 \((1,1)\) 走到 \((2,5)\)。这两行不需要相邻,只要存在这样的两行就一定不合法。因此得出一个更强的约束:对每一行,把占据区间提取出来,任意两个区间必须是包含关系,并且从上往下看,区间长度一定是先递增再递减的。此时也不难验证充分性。到这里图形的约束已经非常强了。

然后考虑直接 dp:设 \(f(x,y,l,r)\) 表示我们限定图形范围在行 \([x,y]\) 内,且长度最小的那一行区间是 \([l,r]\) 能得到的最大图形。

转移的时候枚举是第 \(x/y\) 行的区间为 \([l,r]\),然后转移到 \(f(x,y-1)\) 或者 \(f(x+1,y)\)\([l',r']\) 状态上去,满足 \([l,r]\subseteq[l',r']\) 即可。

这样我们需要算出某个 \(f(x,y)\) 后对他的信息做二维前缀 \(\max\)\(O(1)\) 转移。复杂度 \(O(n^4)\) 还是太慢。

考虑优化状态数:固定 \([x,y]\) 以后 \([l,r]\) 一定满足:第 \(x\sim y\) 行上,\([l,r]\) 范围内都是空地。令 \(b_c\) 表示 \(x\sim y\) 行内第 \(c\) 列是否全为空地。则条件可以表述为 \(b_{l}\sim b_{r}\) 全为 \(1\)。注意到 \([l,r]\) 应该贪心地取到一段极长的 \(1\) 的连续段,这样的话固定 \([x,y]\) 后就只有 \(O(n)\) 个状态。状态数缩减到 \(O(n^3)\)

还能继续优化:我们只固定 \(x\),然后如果一个 \((y,l,r)\) 满足说,第 \(y+1\) 行的 \(l\sim r\) 列依旧都是空地,那么它就不是极大的。或者说第 \(x\sim y\) 行的第 \(l-1/r+1\) 列都是空地,那么也不是极大的。然后极大的状态应该只有 \(O(n)\) 个:令 \(b_c\) 表示从第 \(x\) 行开始,第 \(c\) 列的第一个障碍位置。那么 \((y,l,r)\) 合法当且仅当 \(y\) 是区间里 \(b\) 的最小值减一,且 \(b_{l-1}/b_{r+1}\) 都小于这个区间最小值。那么很显然就能分析出只有 \(O(n)\) 个极大状态了。 这样我们就保留了 \(O(n^2)\) 个极大状态,那么转移呢?

只对每个极大状态存储 dp 值。讨论到底是第 \(x\) 行的区间等于 \([l,r]\) 还是第 \(y\) 行的区间等于 \([l,r]\)

对于前者,我们应该转移到某个 \((x+1,y,l',r')\) 上去。对于后者,我们应该一口气把第 \(y\) 行这边的,连续极长的只能取 \([l,r]\) 的行全部选了。也就是 \(\ge \max\{b_{l-1},b_{r+1}\}\) 的行。然后转移到某个 \((x,y',l',r')\) 上去。

固定 \((x,y)\) 以后,所有的极大状态里 \([l',r']\) 肯定都是不交的。所以对于 \(x/x+1\) 和每个 \(y\),把所有极大状态按照 \(l'\) 升序排序并储存其 dp 值。然后询问的时候只需要二分查找一次,就是 \(O(n^2\log n)\) 的复杂度。可以轻松通过。

代码

Day2 T1. 山毛榉树

可以说是本场比赛最为 ad-hoc 的题,遇到这种脑力体操只能说自求多福了。

首先绝妙置换应该是一个拓扑序。同时一个节点不应该有两个相同颜色的儿子,因为这两个儿子的 \(f\) 不同而父亲相同。

对于一个固定的颜色 \(c\),考虑所有颜色为 \(c\) 的儿子,由于 \(f\) 是递增的,所以一定是一段前缀,有颜色为 \(c\) 的儿子。

考虑对于两个点 \(x,y\)\(x\) 先于 \(y\) 出现。然后对于一个儿子的颜色 \(c\) 只有三种情形:都有,都没有,\(x\)\(y\) 没有。后两种情况平凡,对于第一种情况,我们会要求 \(x\) 的儿子先于 \(y\) 的儿子出现。那么也就是递归定义了一个偏序关系的存在:

\[cmp(x,y)=\prod_{c}[cmp(son_{x,c},son_{y,c})] \]

当然这里忽略了不能有 \(x\) 没有但 \(y\) 有的颜色,这个是容易判断的。

然后我们断言说置换合法等价于任意相邻的点都满足 \(cmp\) 这个偏序。容易说明充分性。

但是你这个判断不能真 dfs 下去,肯定复杂度爆炸。发现 \(cmp\) 成立的必要条件是 \(sz\) 有偏序关系。那么我们按照 \(sz\) 先排序。然后 check 的时候只需要比较儿子颜色集合是否相同,以及儿子的 \(sz\) 是否有偏序关系即可。

然后就启发式合并一下就 \(O(n\log^2 n)\) 了。

代码

Day2 T2. 超车

这个题还是比较平凡的,也很吃实现功力啊啊啊。

首先题目其实已经给你转化好很多东西了:最朴素的想法是每次询问重新做。然后按照题面里的过程模拟,也就是按照到达时间为第一关键字速度为第二关键字的顺序排序,然后求前缀到达下一个站的时间的 \(\max\),这样就是 \(O(qmn\log)\) 级别的。

然后说,我们不需要每次把备用车塞进去,因为备用车影响到的车,一定速度都比他快,那么这些车都不会影响备用车。

换言之就是,你直接预先把 \(n\) 辆一般车求一遍每个站的到达时间。然后一次询问的时候就直接 \(O(m)\) 扫每个车站,二分+求前缀 max 就可以求出到下一个车站的到达时间,这样的话复杂度是 \(O(n^2\log+qm\log)\) 级别的。已经能通过除了最后一个子任务以外的所有测试点。

进一步的话考虑只能是预先求出每个 \(y\) 对应的答案。就是维护那种 \(ans(y)\) 的数组之类的。因为这东西交互等价于强制在线,也没有什么离线下来的说法。那就考虑最初 \(f(y)=y\)。到达一个车站继续走的时候,对于 \(n\) 俩车,他们应该有一个管辖区间 \([l,r]\),就是当 \(y\in [l,r]\) 的时候,对应的前缀 max 是这一辆车。然后会把这个区间的一个前缀强行赋值成前缀 max,剩余部分不变(因为它们的到达时间自身就大于前缀 max 了)。所以每个 \(f(y)\) 都是关于 \(y\) 的一个一次函数。这是我们的大致过程,容易想到用类似颜色段均摊的方式,set 维护。每一段的 \(f\) 都是一个固定的一次函数。

那么一个问题是,你枚举了一辆车怎么快速找到它的管辖区间?可以在平衡树上二分,太不牛了。对每个连续段把它的结尾的 \(f\) 扔进一个 map 里然后在 map 上二分一下就能找到第一个被管辖到的连续段,这个写法比较方便。

时间复杂度即为 \(O(n^2\log+q\log)\)

代码

Day2 T3. 机器人比赛

呃呃这个题先咕了。


WC2024

🥉。

T1. 代码堵塞

签到题。

考虑有两种情况:

  • IOI 赛制的没拿完就时间到了。
  • IOI 赛制的拿完了,还拿了一部分 OI 赛制的。

第一种情况就直接正着背包一下就好了,记录一下当前的总用时,和 \(T+1\)\(\min\)

第二种情况的话,一定是一个前缀的题被干掉了,这个前缀可以是 IOI/OI 任选。然后后缀里剩下的都是 IOI 赛制的题。枚举一下极长前缀的长度 \(p\) 然后预处理一下后缀背包信息即可。

时间复杂度 \(O(nT)\)

代码不放了,场上写的弱智题还再写一遍?

T2. 水镜

🥉。

首先考虑如何判定一个区间合法?

不知道为什么这个题有 \(2L\)。 令 \(L:=2L\),容易看出,选 \(b_i := h_i\) 的位置一定构成上升子序列,选 \(b_i := L-h_i\) 的一定构成下降子序列。

因此一个区间能被拆成上升/下降子序列是必要条件,但其实不充分。

为什么?比如说如果两个位置 \(i\lt j\),前者不对称后者对称,则实际上要求 \(L-h_j\gt h_i\)。如果是前者对称后者不对称,则实际上要求 \(L-h_i\lt h_j\)。因此 \(L\) 是有上下界的。那么如果上界小于等于下界了就依旧无解。

很棘手的是我们看似要研究拆分方式,还要让拆分方式得到的上下界合理,这个东西看着就没法做。但是可以发现说,你的下界始终是 某个 \(h_i+h_j\)。所以我们只需要 \(n^2\) 个可能的 \(L\) 就够了,形如 \(h_i+h_j+0.5\)。然后你直接枚举 \(L\) 以后很容易判断每个区间是否合法。这样得到一个 \(n^3\) 的做法,至少多项式了。

一般来说 \(n^3\rightarrow n^2\) 都不难。就是你注意到我们对 \(L\) 做出上下界要求的时候。我们只需要对每个 \(i\)\(b_i\lt b_{i+1}\) 即可。因此 \(L\) 只会被 \(h_i+h_{i+1}\) 这种东西约束到。然后可能的 \(L\) 就只有 \(O(n)\) 个了。此时拿下 \(44\) 分,距离🥈非常接近了。

把可能的 \(L\) 拿出来设为 \(v_1,v_2,..,v_m\)。接下来换个维。设 \(f(i,0/1,j)\) 是当 \(L=v_j\)\(b_i=h_i/L-h_i\) 的时候,向后能延申到的最大值。

朴素的 dp 还是 \(O(nm)\) 的,你注意到其实如果我们把它看成是 \(f(i,j)\),然后信息是一个大小为 \(2\) 的向量。转移是在乘 \((\min,+)\) 矩阵,拿个线段树维护一下矩阵乘法就好了。时间复杂度 \(O(n\log n)\),带常数 \(27\)。实现的不好肯定要被卡成 \(76\),但这个分不用做 T3 都已经 🥈 了。 有两个卡常,一个是不要封装矩阵类,直接把矩阵写进线段树里。第二是大家都喜闻乐见的循环展开。然后可以轻松 600ms 左右通过。

代码

T3. 线段树

🥉。

场上没做出来确实认了,但是没把 \(n2^m\) 修出来有点唐诗的。

先考虑线段树上的每个节点是否能被表示出来。这里遵循一个基本原则:

  • \(u,lc(u),rc(u)\) 三者知二求一。

特殊性质等价于线段树的每个叶子信息都能被问出,也就等价于每个节点的信息都能被问出。

因此如果我们能做这样一个问题,可以轻松拿下特殊性质(但其实也只能拿下特殊性质):钦定线段树上的一些节点必须能被表示出。问有多少种节点的集合能满足这个条件。

考虑 dp。具体而言每个节点有三种状态。

  • 钦定他不能被表示 (0)
  • 仅用子树内的信息就能表示出他 (1)
  • 仅用子树内的信息不能表示出他,但是通过子树外的信息(也就是知道了父亲和兄弟)可以表示出他(2)

然后设 \(dp(u,0/1/2)\) 表示确定了每个 \(u\) 子树内每个节点的状态,并且 \(u\) 节点本身的状态为何,这样的一个方案数。

转移需要不小的分类讨论,致使我场上认为这就是接近正解的东西。方便起见可以预处理 \(3^3\) 的数组 \(w(x,y,z)\)。表示左儿子状态为 \(y\),右儿子状态为 \(z\),转移到节点 \(u\) 状态为 \(x\) 的系数。其实非 \(0\) 项并不多。然后我们可以结合暴力获得 \(25\) 分。

接下来考虑 \(m=1\),到这里开始进入到最棘手的环节。

就是,比如说这样一个结构:

1 -> 2 -> 4
	   -> 5
  -> 3 -> 6
  	   -> 7

我们想要知道 \(5,6\) 两个节点的并,这个区间的信息。我们并不需要知道 \(5,6\) 两个节点的信息。我们可以用 \(1\) 节点的信息减去 \(4,7\) 节点的信息来获取。这是问题所在。

首先,我们给每个节点打上 \(0/1/2\) 表示它的状态。\(1,2\) 都表示这个节点的信息是知道的,意义和上面的一样。然后,对于一个子树 \(u\) 有两种情况:

  • 我们知道 \(u\) 子树内,\([ql,qr]\) 相关的所有信息。(0)
  • 我们不知道 \(u\) 子树内,\([ql,qr]\) 相关的所有信息。但我们知道补集的信息。(1)

特殊地,若子树代表的区间和 \([ql,qr]\) 无交则始终认为是 \(0\) 状态。

以上面的例子为例:当 \(u=2\) 的时候,第一种情况意味着我们知道节点 \(5\) 的信息。第二种情况意味着我们不知道节点 \(5\) 的信息,但知道节点 \(6\) 的信息。合并时这个 bit 会发生如下转变:

  • 如果两边子树的 bit 都为 \(1\),且我们知道当前节点 \(u\) 的信息,那么新的 bit 就是 \(0\)(用整体减去补集)。
  • 如果有一边的子树的 bit 为 \(1\),而兄弟子树和 \([ql,qr]\) 的交为空,且我们不知道兄弟节点的信息,则一定不合法。
  • 否则就是,\((0,0)\rightarrow 0\)\((0,1)/(1,0)\rightarrow 1\)

在 case2 中,会不会出现我们既知道 \(u\) 的信息也知道 \(v\) 的兄弟节点的信息的情况?这意味着我们同时也知道 \(v\) 节点自身的信息。那么你在 \(u\) 这里使用补集转化,不如在 \(v\) 那里使用。所以不会出现这种情况。

所以我们在特殊性质的 dp 基础上,多加一个 bit \(0/1\) 即可。合并的时候枚举 \((lc,0/1/2,0/1)\)\((rc,0/1/2,0/1)\) 再套用上面的合并方式。注意到:当且仅当第二维是 \(0\) 的时候 bit 才可能为 \(1\)。这个时候其实要求左右子树的 bit 都为 \(1\) 才行(否则会被上面的第二条干掉)。

更进一步地,\(m\) 个区间的计算是独立的。所以我们完全可以维护大小为 \(2^m\) 的第三维,做到一个 \(O(n2^m)\) 级别的状态 \(dp(u,0/1/2,S)\)。转移的时候,两边的 \(S\) 必须完全一致(或者干脆第二维是 \(1/2\),不存在 \(S\) 这个东西)。所以时间复杂度也是 \(O(n2^m)\) 的。结合前面的内容可以获得 \(55\) 分。

这个 task 是我认为到正解最难的一步,很有必要写出来以后再思考进一步的优化。

接下来着眼于缩减状态数。发现 \(S\) 集合的变化很有规律:

  • 要么是两颗子树的 \(S\) 相同,然后变成 \(S\) 并上节点 \(u\) 挂着的编号集合。
  • 要么是仅有一颗子树存在 \(S\),另一个子树的根节点信息已知。此时还是变成 \(S\) 并上节点 \(u\) 挂着的编号集合。
  • 否则 \(u\) 节点已知,\(S\) 变为不存在(此时第二维变成 \(1/2\) 了)。

发现转移到 \(u\) 的时候 \(S\) 节点就是在一路向上并上路径上每个节点挂着的编号集合。断言:\(S\) 一定是 \(u\) 到子树内某个叶子的编号集合并。不难归纳证明。然后做完了啊?你就设 \(dp(u,1/2)\) 或者 \(g(u,0,x)\),其中 \(x\) 一定是 \(u\) 的某个叶子。

但是有个问题是可能两个不同的 \(x\) 导出相同的集合 \(S\)。为了判断这个,我们 xor-hashing 一下即可:直接计算出 \(1\rightarrow x\) 的路径上所有节点挂着的询问的权值并即可。所以全局本质不同的集合也就不超过 \(n\) 个。离散化以后直接设 \(g(u,0,idx)\) 表示 \(S\)\(idx\) 这个标号代表的集合,去掉 \(1\rightarrow fa_u\) 路径上的询问所构成的就好了。转移的时候大概是若两颗子树都在某个 \(idx\) 处有值,就会相乘贡献给 \(g(u,0,idx)\),还有一类转移是乘上和兄弟的 \(f\) 有关的一个系数,再去加给 \(g(u,0)\)。我们直接把共有的位置的值全部取出来,然后跑线段树合并。再把这些值扔进线段树里加一下即可。根据启发式合并的分析复杂度就是 \(O(n\log^2 n)\) 的。

最后一个小细节是,算 \(1\rightarrow u\)(其中 \(u\) 是某个叶子)的权值 xor 和,转成原序列就是在算包含 \(u\) 的询问的权值 xor 和。直接差分一下即可。总复杂度即为 \(O(m+n\log^2 n)\)

代码

USACO24 JAN

可能是最近打的最好的比赛了。补一下题解,我觉得题目质量还是挺高的。

P1. Island Vacation

可能所有人或多或少都有想法,但是越想越乱,越写越写不明白?感觉是很考验耐心和基本功的题。

先考虑建出圆方树:然后我们考虑从 \(1\) 为根开始,最后停留在一个点 \(u\) 的过程到底是什么样的?

首先我们得从 \(1\) 走到 \(u\)。这个过程中,考虑圆方树上的 \(fa\rightarrow u\) 这条边。

  • \(fa\) 是圆点,那么 \(fa\rightarrow u\) 这条边必须被走过。
  • \(fa\) 是方点,则考察 \(fa\) 代表的环。有两条边和 \(u\) 相邻。我们一定是走了其中的一条来到 \(u\),并且很显然不能再从 \(u\) 出发走另外一条了,因为这样就不可能回到 \(u\)

需要特判点 \(1\)

然后,再考虑第一次到达 \(u\) 以后,行走若干步回到 \(u\)

  • 我们显然不会走圆圆边。因此一定是走进 \(u\) 的若干方点儿子,然后进入这个环,转一圈后回来。

在这个环上转一圈的过程中,我们考虑如果环上的某个点还挂着其它的环,那么也可以进一步递归下去,然后转一圈再上来。

发现这个过程没有那么简单,是一个不断递归下去的事情。因此需要预处理出一个量 \(R_u\),其中 \(u\) 是圆点且 \(u\) 的父亲是方点。定义 \(R_u\) 为:

  • 通过 \(fa\) 所在环的一条边第一次进入 \(u\)。然后在 \(u\) 内部走若干步,最后回到 \(u\),再从 \(fa\) 所在环的另外一条边离开。这个过程发生的概率。

如何计算 \(R_u\)?我们依旧无法走进 \(u\) 的圆点儿子。因此考虑拉出所有方点儿子进行 dp。我们选出一个方点儿子的子集 \(S\),表示整个游走过程中进入了这些环。显然先进入哪个环都无所谓,所以有 \(|S|!\) 种方案,然后每进一个环再走回 \(u\),剩余的 \(deg\) 都会 \(-2\),所以大概需要维护一个连乘积表示选择这些环进入的概率。需要注意的是一个环还有正反两种走法,所以概率的分子不是 \(1\) 而是 \(2\)。但是不论正走反走,这个环的贡献应该就是环上其余点的 \(R\) 的乘积。因此我们把环视作一个权值为 \(\prod R\) 的物品。则一个集合 \(S\) 的权值为所有物品的权值乘积。我们只关注 \(|S|\),所以直接对所有环做背包即可。

这个背包的复杂度是 \(O(deg^2)\) 的,因此总复杂度是 \(O(n^2)\)。注意到 \(n\le 10^4\) 实际上意味着方点个数 \(\le 5000\)

处理完了 \(R\) 后。容易发现其实第一部分的计算也是类似的:就是说,我们从 \(u\) 出发游走若干轮后再选择一个儿子走下去即可。那么唯一的区别就是如果我们选择了一个环走下去,则 \(S\) 的选择中不能包含这个环。那么做一次背包撤销即可,复杂度还是 \(O(deg^2)\) 也就是 \(O(n^2)\) 不变。

代码

P2. Merging Cells

考虑一个比较经典的套路是合并改成分裂:也就是我们设 \(f(i,j)\) 表示说有多大的概率使得最后生成的细胞由 \([i,j]\) 内的细胞主导。所求即为 \(f(i,i)\)

那么首先 \(f(1,n)=1\)。接下来考虑枚举一个区间 \([i,j]\),然后枚举断点 \(k\)。比较 \([i,k]\)\([k+1,j]\) 的优劣,决定贡献给哪一者。系数是组合数。

这样得到一个 \(O(n^3)\) 的做法,已经可以通过绝大多数测试点。

考虑优化:比方说 \((i,j)\rightarrow (i,k)\)。那么首先要求 \(k\)\(\lt j\) 的一段后缀,且注意到系数为 \(P_{j-i-1}^{j-k-2}=\frac{(j-i-1)!}{(k-i+1)!}\) 注意到分母只和 \((i,k)\) 有关,分子只和 \((i,j)\) 有关。因此容易双指针 + 前缀和优化做到 \(O(n^2)\)

代码

P3. Mooball Teams III

基础题。

考虑容斥:计算出能被水平/竖直分开的,再减去两种方式都可以的。

前者很容易:以水平为例,枚举下半侧的 \(\max y\) 的值,然后统计出多少点的 \(y\) 坐标大于这个值即可。

后者的话:依旧考虑枚举 \(\max y\) 的值,那么唯一对应一个点 \(u\) 必须被选中。然后讨论两种情况:

  • \(u\) 位于左半侧。
  • \(u\) 位于右半侧。

两种情况本质相同,下面仅考虑前者。我们再枚举右半侧的 \(c:=\min x\) 的值。则相当于:

  • 首先 \(x=c\) 对应的点 \(v\) 必须满足 \(y_v\gt y_u\)。然后点 \(c\) 必选。然后 \(x\lt x_u\land y\lt y_u\) 或者 \(x\gt x_u\land y\gt y_v\) 的人可以自由决定是否选择。若选择则左右两个点集恰好能进入一个。

固定 \(u\) 以后,满足条件一的点数是定值。我们只关注满足条件二的。令 \(f(v)\) 是对于一个 \(v\) 有多少个点满足条件 \(2\)。从小到大枚举 \(\max y\) 的过程中相当于 \(f(v)\) 的前缀减一。若我们维护 \(2^{f(v)}\) 的区间和,那就相当于是区间乘 \(2^{-1}\),区间求和。

然后使用线段树,在 \(O(n\log n)\) 的时间内解决此题。


ARC131

这场是闲暇时间做了一下,感觉难度略大于 ARC132。

D. AtArcher

直观感受一下:两边的分布应该相对均匀。事实上,对于一般情况,设最左边的坐标为 \(x\lt 0\),最右边的坐标为 \(y\gt 0\),则 \(|x|\)\(y\) 的差值不会大于\(d\)。否则我们把绝对值大的一端移动到另一端不会让得分变低。

也就是说可能的排列方式只有 \(O(d)\) 种:我们把中心位置放在一个 \(-d\) 的位置然后整体向右不断滑动,滑动 \(O(d)\) 步后就一定能得到最优解。我们只需要对这个滑动窗口的每个时刻,去计算此时的得分。

这个计算看似很难,因为每个点都在变化,但是你发现:对于一个分界点 \(i\),只会有 \(O(1)\) 个点从这个分界点的左侧移动到分界点的右侧。因为两个点的距离始终保持为 \(d\),所以若两个点接连跨越这个分界点,就说明移动量至少增加了 \(d\)。因此,虽然一个点在整个过程中跨越分界点的次数不定,但是对所有点求和的话,这个次数是 \(O(m)\) 级别的。因此我们就在 \(O(n+m+d)\) 的时间内解决了这个问题。

值得一提的是若 \(d\) 很大一样可做:我们相当于是有 \(O(m)\) 个事件,每个事件位于一个时刻并且有一个权值。然后要找一个权值和最大的前缀。离散化下来以后就是 \(O(n+m\log m)\)。不过在本题不需要。

代码

E. Christmas Wreath

第一眼看上去毫无思路,先找必要条件:显然有 $ n\not\equiv 2\pmod3$。以及 \(n\le 3\) 时无解。猜测其余情况必定有解。也就是 \(n=(6,7),(9,10),(12,13)....\) 时一定有解。

样例甚至没有愿意给一个有解的输出,那么构造出的解应该很有启发性。搜索 \(n=6\) 时的答案,第一个解就很好看:

RRRRR
BBBB
WWW
WW
B

发现一行内所有的字符都相等。首先如果满足这个条件,那么一定不会有颜色互不相同的三元环。所以我们尝试变成这样一个问题:把 \(1\sim n-1\) 划分成 \(3\) 组使得和相等。因为猜测一定有解所以可以 dp 。不过欲证明之还有更简单的方法:首先 \(n=6/7/9/10\) 容易手动构造。否则在 \(n-6\) 的基础上把 \((n-6,n]\)\(6\) 个数划分为 \(3\) 组相等的,分别加入三个组即可。

代码

F. ARC Stamp

非常高妙的题,希望我能有自己想明白这种题的一天。

如何判断一个 \(S\) 能得到 \(T\)?我们称选取三个字符变为 ARC 的操作为“覆盖”这三个位置。

当两次覆盖的位置有交的时候,就会在 T 中表现出 ARARC 或者 ARCRC 这种形态。

称一次被覆盖的三个位置是”一段“则一段可能上面会有别的段盖住它,但至少应该有一个位置没有被盖住。

如果完全没有被盖住那就是 ARC 显示在外,如果一个前缀被盖住就是 RC/C 显示在外,后缀类似。还有可能是两边的字符被盖住,也就是 R 显示在外。除此以外没有别的情况。

因此考虑若两个段有交(相邻不算有交)就合并。那么每一个段的连续段都应该长成这样:PRPR...RPRP

其中 R 就是字符 R。而 P 是形如 ARC,然后右侧接若干 RC/C,左侧接若干 A/AR

然后回到串 \(T\) 的视角:把 T 按照这种方式做划分:就是每个 ARC 为中心出发,左右两侧拓展 A/AR 或者 R/RC。然后如果一个 R 的左右两侧都被拓展到,那么它也被拓展到。然后不能被拓展的位置,是 \(S\)\(T\) 中都应该保持相同的,所以缩在一起变成一个不能动的状态,记作 X

比如说原串是 CCCARAARCCRAARC,我们就化成 (X)(AR)(A)(ARC)(C)(R)(A)(ARC) 的形式。

接下来考虑计算答案:就是说一个 \(S\) 合法,那就是选择 \(\le k\) 个非 (X) 的括号位置,其余位置和 \(T\) 保持一致。然后选择的位置可以任意抉择,最后都要变成 ARC,并且覆盖顺序也有优先级,就是跟 \(T\) 中表现出来的顺序一致。

那么发现这个东西其实非常错,它既没有考虑到会算重,也缺少了一些必要性的约束:比如 (AR)(ARC),你是不可能选择 (AR) 而不选 (ARC) 的。那我们先来思考一下必要条件:

  • ARC 最自由,想选就选,不选也罢。
  • A/AR 想选的话,那么右边的必须选。
  • C/RC 想选的话,那么左侧的必须选。
  • R 想选的话,两侧的都必须选。

然后再来考虑算重这个事情:就是你考虑 ARC 这个位置,如果我只单独选择它,那么 \(S\) 中这个位置就不能是 ARC 本身,因为这样的话我再去覆盖这三个位置就没有意义。但是如果两边的位置,选了至少一个,那么根据顺序应该是先执行两边的覆盖再执行这里的覆盖,此时这里是原本是 ARC 的话就也是有意义的。因为旁边的覆盖完以后中间的就不是 ARC 了,然后你再覆盖成 ARC,那么原本这里是什么都不会导致无意义的操作。

然后 A/ARC/RC 显然是相反的,我们只考虑 A/AR 的情况,对应位置分别有 \(3/9\) 种可能,若其左侧的位置选了,则这些可能就都能取到,否则一定初始一定不能是 A/AR 本身,对应位置就只有 \(2/8\) 种可能。

对于 R:其初始值无论如何一定不能是 R 本身,因此始终只能是 \(2\) 种可能。

到这里还发现一个问题:ARCARC 这个串就算两边都选,也只有 \(26^2\) 而非 \(27^2\) 种可能,明明两个位置都满足”旁边的位置也选“啊?这是因为这两个段互不相交,因此没有任何干涉:所以在初始对 \(T\) 划分的时候,如果两个相邻的字符都能被覆盖,且来源于不同的 ARC,那么我们在之间强行插入一个 (X) 表示这两段毫不相干。

这样分析完了以后就可以 dp 计数了:可以设 \(dp(i,j,0/1,0/1)\) 表示算完第 \(i\) 个括号,有 \(j\) 个选择了。然后第 \(i\) 个是否选了,第 \(i+1\) 个是否钦定必选。这样就可以转移了。

然后发现我们其实只关心是否第 \(i\) 个被选且第 \(i+1\) 个钦定备选,所以其实可以进一步只记录一个 \(0/1\)

时间复杂度 \(O(|T|^2)\),常数很小。

代码


ARC132

随便 vp 了一场看起来简单一些的 ARC 找找状态。

D. Between Two Binary Strings

首先考虑 “between” 这个条件的形式化定义:我们取出两个串中所有 \(1\) 的位置 \(a_1,a_2,..,a_m\)\(b_1,b_2,...,b_m\)。 则答案的第 \(i\)\(1\)

必须位于区间 \([\min(a_i,b_i),\max(a_i,b_i)]\) 内,这是一个串 “between” 的充要条件。

把约束写为 \([L_1,R_1],...,[L_m,R_m]\) 的形式,注意到总有 \(L_i\lt L_{i+1}\) 以及 \(R_i\lt R_{i+1}\)

”连续的相同“和”连续段数“是互补的关系:若我们 \(2\times 2\) 地枚举开头和结尾的值。则我们知道了 \(1\) 的连续段数,就知道了总的连续段数,也就知道了 \(s_i=s_{i+1}\) 的个数。现在就是要求最小化 \(1\) 的连续段数。

考虑直接把 \(1\sim m\) 分成尽可能少的段,使得每一段都能靠拢在一起(\(L,R\) 的双严格单调性使得我们可以对划分成的每一段独立考虑它们是否能聚拢)。对于第一段和最后一段可能会有特殊约束,因为我们钦定了开头和结尾的字符。对于一般的段,我们假设把 \([x,y]\) 都聚拢到了一起,且设第 \(y\)\(1\) 位于 \(p\)。则我们相当于是:\(p-(y-i)\in [L_i,R_i]\) 必须成立,其中 \(i\in [x,y]\)。则我们只关注区间中 \(L_i-i\)\(R_i-i\) 的极值,用 st 表维护区间 rmq 后,容易 \(O(1)\) 判断 \([x,y]\) 是否能聚拢。

又有:若 \([x,y]\) 能聚拢,则 \([x+1,y]\) 一定能聚拢。因此我们设 \(f(i)\) 是前 \(i\)\(1\) 划分出的最少段。转移时先而分出最小的 \(j\) 使得 \([j,i]\) 可以聚拢,然后此时相当于查询 \(\ge j-1\)\(f\) 的最小值,使用 bit 便容易维护。时间复杂度 \(O((n+m)\log m)\)

有一个 corner case 是当 \(m=0\) 时我们需要特判:因为在我们钦定开头/结尾有 \(1\) 的时候,虽然不合法,但是由于 \(m=0\),我们调用 \(f(0)=0\) 作为答案,会误判成合法。

代码

E. Paw

上来先考虑说,枚举一个位置计算它变成 < 的期望。越做越不对劲,这也能做?逐渐感觉到这个方向有问题。

那就得换个想法,但是期望题还能怎么换做法?只能启动手玩了。

手玩了一下,发现这个串好像最后一定形如:存在一个 \(i\) 使得第 \(i\) 个洞和第 \(i+1\) 个洞之间的方格没有被踩过。然后 \(\le p_i\) 的方格都是 <\(\ge p_{i+1}\) 的方格都是 >

如何证明?考虑归纳。设 \(m\) 为洞的个数,当 \(m=0\) 时显然成立。当 \(m=1\) 时,枚举最后一个被填上的洞 \(x\)。不妨设其选择向左滚动。则其左侧所有位置最后都会是 < 且我们并不关心左侧剩余洞的方案。对于 \(x\) 右侧的部分,递归到了一个 \(m\) 更小的情况。把 \(x\) 这侧的 <<<< 与右侧递归得到的 <<<...>>> 拼接,一定得到一个更大的 <<<...>>> 的形式。

现在,考虑枚举这个 \(i\) ,然后 < 的个数就已知,只需要计算这个局面出现的概率。

相当于是:左侧的一个洞如果向右滚,不能滚出 \(\gt p_i\) 的位置,右侧的一个洞如果向左滚,不能滚出 \(\lt p_{i+1}\) 的位置。显然左右两侧合法的概率是独立的,并且两边是本质相同的问题。只关注这一侧的洞数。因此我们可以设概率为 \(f(m)\),表示左侧有 \(m\) 个洞的情况。

\(f(m)\) 寻找一个更形式化的定义:我们按照激活顺序反过来考虑每个洞,也就是初始每个洞都被填上,然后考虑每次选一个洞挖开。若一个洞的右侧已经有洞被挖开,则它的选择是无所谓的。否则,它就只有 \(\frac{1}{2}\) 的概率向左滚。因此,\(f(m)\) 可以看作是下面问题的解:

  • 对所有 \(m\) 排列 \(q\),求 \(\sum_{q}2^{-F(q)}\) 的和。其中 \(F(q)\) 是该排列的前缀最大值数量。

考虑增量计算 \(f(m)\):我们枚举 \(m\) 前面有 \(i\) 个数,则并不关心这 \(i\) 个数是谁,只需要用到 \(f(i)\) 的信息。因此有:

\[f(m)=\frac{1}{2m}\sum_{i=0}^{m-1}f(i) \]

初始值 \(f(0)=0\)。于是可以 \(O(n)\) 算出 \(f\)。整个问题也在 \(O(n)\) 时间内得到解决。

代码

F. Takahashi The Strongest

这个题应该是没有 900 分的难度的。

注意到获胜等价于钦定 \(A,B\) 两人在这一步都出被克制的那个决策。考虑枚举一个集合 \(S\),计算集合 \(S\) 内的步骤内获胜的方案数,容斥系数为 \((-1)^{|S|}\)

那么我们就需要统计 \(A,B\) 两人的决策内有多少满足 \(S\) 带来的约束。约束实际上是什么?就是有些位置钦定了取值,还有些位置不钦定。

那么一共就有 \(4^k\) 种约束。我们先对于 \(4^k\) 种约束,求出符合约束的 \(A,B\) 数量。相乘再乘上 \((-1)^{|S|}\) 放在位置 \(S\)。然后再做 sos dp 即可。

这个 sos dp 的形式依旧是按顺序枚举 \([0,k)\) 这些维度,然后一个维度的取值有 \((0/1/2)+3\) 四种可能,我们会从 \(3\) 贡献到 \(0/1/2\)。复杂度是 \(O(k4^k)\) 的。

那么统计符合约束的 \(A,B\) 数量也是相同的 sos dp 手法:只不过是从 \(0/1/2\) 贡献到 \(3\)。然后整个问题就在 \(O(k4^k)\) 的时间内得到解决。

代码


正睿2024省选十连 Round 8

A. 皮卡丘

这个是三道题里最抽象的。。我感觉很神秘啊。

考虑若一家店无法看到别人,也没有贴海报。一定不是最优的。换言之最优解中每家店若没有贴海报,则一定能看到对岸的一张海报。我们称一个店为合法的,当且仅当其要么其未贴海报,要么其能看到对岸的一张海报。

我们的目标即为选中权最小的店的集合贴海报,使得所有店合法。

\(f(i,j)\) 是我们让北侧 \(\le i\) 和南侧 \(\le j\) 的店都合法的答案。所求即为 \(f(n,n)\)。这个状态看上去非常难转移,但其实可以实现之:

考虑北侧店 \(i\) 和南侧店 \(j\) 分别因谁而合法(自己,或者对面一个张贴海报的位置),然后有以下情况:

  • 两者的决策点是同一家。这要求 \(|i-j|\le d\)。然后比如说我们在北边的 \(i\) 放了海报,则北边 \(i\) 和南边的 \(j\) 都因为这张海报而合法。如果放在北边则转移到 \(f(i-1,i-d-1)\),放在南边则转移到 \(f(j-d-1,j-1)\)
  • 两侧的决策点并非同一家,此时我们枚举其中一家的决策点,这里还分是自己贴海报和对岸贴了两种。以北边为例,自己贴的话就是转移到 \(f(i-1,j)\),别人贴的话就是说枚举对岸的一个位置 \(|k-i|\le d\)(这里不要求 \(k\le j\))并且转移到 \(f(k-d-1,j)\)

这样的话就得到一个 \(O(n^2d)\) 的做法,真菜。

比较能感受到如果 \(|i-j|\) 太大是不优的:事实上若 \(|i-j|\gt 2(d+1)\) 则这个状态一定无用。所以缩减到 \(O(nd^2)\)。更进一步观察到转移可以用单调队列优化,变为 \(O(nd)\)

想了很久我能从这个题吸取什么经验,最后的结论是这个题有非常多的无法优化的 \(O(nd^2)\) 简单做法,赶快写了就该去做下一个题了。

B. 米老鼠

为啥你不是 T1 呢?

首先将询问的 \([l,r]\) 差分为 \(\le r\)

令起始位置为 \(q\)\(p=q\) 的情况是平凡的,不妨考虑 \(p\lt q\) 的情况。

此时令 \(x := \max_{i=p}^{q-1}a_i\),然后令 \(t\)\(q\) 右侧(不包含 \(q\))第一个 \(a\gt x\) 的位置(若不存在则设为 \(n+1\))。则发现我们走到 \(p\) 的步数就应该是 \(t-p\)

考虑枚举 \(p\) 后,找出序列 \(a[p,n]\) 的所有前缀最大值位置,拉出来得到一个单调栈。

\(q\) 不在单调栈上,则 \(t\) 就是 \(q\) 在单调站上的后继。因此在单调栈上二分出一个最靠后的 \(t\) 满足 \(t-p\le r\) 然后就能统计出此类 \(q\) 的个数。

否则 \(q\) 在单调栈上,考虑合法性也是有单调性的,因此可以在单调栈上二分出一个最靠后的合法的 \(q\)。那么此时 \(x\) 的值就是单调栈的上一个位置代表的值,\(t\) 呢?我们发现其实我们就是在寻找一个位置后面第二个大于他的位置,容易预处理之。然后在 \(O((n+q)\log n)\) 的时间内解决本题。

C. 四条珍妃

为啥你不是 T2 呢?

考虑两个特殊性质的做法。

先考虑 \(A\) 操作全在 \(B\) 操作之前的做法。那么给出一个串如何计算他有多少子串合法?

题目中有一个很重要的性质是 \(A\) 串没有互为前缀的串。因此对于 \(B\) 中的一个串 \(S\) 的某个位置 \(i\),至多只存在一个 \(j\) 使得 \([i,j]\)\(A\) 中出现。

则我们可以连边 \(i\rightarrow j+1\),然后得到一颗森林。每个点的深度和就是答案。

如何找到这个 \(j\)?建立 AC 自动机即可。

再看 \(B\) 操作全在 \(A\) 操作之前的做法。此时相当于是每加入一个 \(A\) 串,\(B\) 串中的有些 \(i\) 就会从没有对应的 \(j\) 变为找到对应的 \(j\)。那么我们需要支持两个事情:找到这些位置,以及连边后重新统计深度和。后者可以使用带权并查集简单完成。对于前者,我们反转所有串。然后建立 \(B\) 串的广义 SAM。然后加入一个 \(A\) 串的时候,找到其在广义 SAM 内的位置,则其 parent tree 子树内的点就是被更新到的位置。

结合上述两个做法其实很容易得到强制在线的,一般情况的正解:

  • 我们维护 \(A\) 串的 ACAM 和 \(B\) 的广义 SAM。然后加入 \(A\) 串的时候在广义 SAM 的 parent tree 内 dfs。加入 \(B\) 串的时候在 ACAM 上查询。更新广义 SAM 是容易的,更新 ACAM 需要使用二进制分组。然后时间复杂度 \(O(L\log)\)

杂题

杂牙选刷。杂脸选洗。杂饭选吃。杂题选讲。杂学选上。杂水选喝。杂字选打。杂歌选听。杂觉选睡。杂犇选发。 ——chenxia25

省选模拟赛35 C

考虑比较常规的序列分段做法甚至难以让状态数降到 \(O(n^3)\),我们需要另辟蹊径。

首先根据组合意义,我们可以看作是从每一行里选出两个位置,并且要求这两个位置构成逆序对。

如果某一行里,选出的两个位置都是奇数,则就贡献 \(\frac{1}{2}\) 的概率。

如果第一个位置是奇数第二个位置是偶数,那也就是要求第一个位置的值小于第二个位置的值。

如果第一个位置是偶数第二个位置是奇数,考虑容斥:考虑不做约束的情况,减去第二个位置小于第一个位置的情况即可。

然后你发现如果 \(x\lt y\) 我们连 \(x\rightarrow y\) 的话,这张图是一个链状结构:具体而言是所有偶数位置构成一条链。然后每一行都有可能给加一个叶子。

对于一棵树,我们随机一个排列,满足拓扑关系的概率显然是 \(\prod (sz_i)^{-1}\)。在题目里这个结构里,我们也就只关注偶数位置的 \(sz\)

那么一个偶数位置的 \(sz\) 是怎么决定的?第 \(i\) 个偶数的子树大小是 \(i+k\)。其中 \(k\)\(\le i\) 的偶数里有多少个点挂了叶子。到这里我们就能设计一个三方的 dp 了。

\(dp(i,j,k)\) 表示把 \(2i\) 个节点划分了 \(j\) 段,且有 \(k\) 个节点额外挂了叶子。如何转移?

枚举下一段的长度,然后如果要挂叶子,那么就要枚举是在哪个节点上挂的叶子。这个转移很有优化前途。

实际上仔细研究一下权值就能发现是可以组合意义掉的,然后就变成 \(O(n^3)\) 了,这里比较 trivial。

省选模拟赛36 B

为啥这个题没有做出来?

考虑判断 \(x\times (10^k-1)\) 是否含 \(9\) 比较困难,可以转而判断一个数是否不含 \(9\) 且是 \(10^k-1\) 的倍数。然后找到第 \(n\) 大的这样的数即可。

考虑比较常用的确定第 \(n\) 大方式:先从小到大确定位数,然后确定位数后从高位开始逐位限定。这样只需要 \(O(len\times \Sigma)\) 次计算,每次计算形如给出长度,以及钦定一个前缀的方案。然后询问有多少个数满足不含 \(9\) 且是 \(10^k-1\) 的倍数。可以感受到 \(len\) 是不会太大的。

然后这个判定有一个经典的方法:\(k\) 位一分段,求和相加,判是否能被 \(10^k-1\) 整除。

但是 \(k\) 可能还是很大,看似有点鸡肋。错了!注意到这里求和出来的结果必定是 \(10^k-1\) 的倍数,且最多是 \(\frac{len}{k}\) 倍。因此我们可以暴力枚举求和出来的结果 \(S\)。然后此时开始按照 \(\bmod k\) 的顺序对数位分类,按顺序做数位 dp 即可。

每次做数位 dp 的时候,对于 \(\bmod k\) 相同的一类,需要提起计算其总和为 \(x\) 的方案数,这里 \(x\le \frac{8len}{k}\)。然后令 \(dp(i,j)\) 表示考虑了前 \(i\) 类并且进位是 \(j\) 的方案数即可。显然 \(j\) 的上界也是 \(\frac{len}{k}\)

然后这个做法就是 \(poly(k,\frac{len}{k})\) 的,非常快。并且注意到整个过程中我们用到了极其少的高精度运算,因此就算开大 \(k,n\) 也依旧不会增加太大实现难度。

省选模拟赛39 B

去年在 haoba 模拟见过,当时题解都没看懂,是不是菜了?

首先。不妨认为第一步染黑的点一定是 \(1\)。然后变成了一个长度为 \(n-1\) 的序列的问题。其中 \(a_0\)\(a_n\) 都默认染黑。

然后,我们枚举一个局面,其中有 \(x\) 个点被直接/间接地染黑了(不含 \(a_0/a_n\))。它会停留期望 \(\frac{n}{n-x}\) 步后离开,进入下一个局面。每一步的贡献都是 \((x+1)^{t}\)。因此我们只需要算出这个局面出现的概率即可。

出现的概率可以这样算:我们 rand 一个 \(1\sim (n-1)\) 的排列 \(p\)。然后按顺序去强制染黑每个 \(p\) 中的点,然后询问有多少个排列满足出现了我们枚举的这个局面。

不妨考虑枚举最后一次出现我们枚举的局面的时刻 \(y\),此时恰有 \(y\) 个黑点被打上了 \(1\sim y\) 的标号,剩下的黑点必定两侧都是标号(或者为 \(0/n+1\)),然后标号 \(y+1\) 必须打在了某个白点上。再之后的标号就不关心了。

那么我们先把 \(y\) 个有标号的黑色点挑出,这 \(y\) 个黑点的标号是不影响方案数的。然后考虑剩下还有 \(x-y\) 个黑点和 \(n-x-1\) 个白点插空放入。插空进入的数不带标号,我们只需要研究方案数。考虑实际上形如下:

  • 要么插恰好一个黑点。
  • 要么不插任何点。
  • 要么插入 \(\ge 2\) 个白点。

第一条很容易满足:我们选出 \(x-y\) 个空,插入剩余的 \(x-y\) 个黑点即可。

然后变成这样一个问题:\(F(r,c)\) 表示我们把 \(r\) 个数插入 \(c\) 个空,使得不能有某个空恰有 \(1\) 个数。

那么容易使用前缀和,\(O(n^2)\) 计算出所有的 \(F\)。因此枚举 \(x,y\) 后容易 \(O(1)\) 计算概率。因此本题在 \(O(n^2)\) 的时间内解决。

我卡了很久的原因是枚举 \(x,y\) 后直接去枚举了 \(y\) 的连续段个数然后只有 \(1,3\) 两种限制,但此时因为多了一重枚举量就很难去做了。还有就是没有上来转序列,因此为了处理环上的成为了大常数 \(O(n^3)\)

CF739D Recover a functional graph

Petr 大神场上写完这个题的代价:拿到的分没有过 C 的人多。

不是,怎么远古就有人能整出这种题的?

先考虑没有 \(h,l=-1\) 的构造。考虑按照所在连通块的环长来分组。然后每一组里里有两种点:

  • 必须在环上的(也就是 \(h=0\))点。此类点的数量必须是环长的倍数。(1)
  • 在环外的(也就是 \(h\gt 0\))点。则假设有一个点的 \(h\)\(a\),就必须要有一个点的 \(h\)\(a-1\)。(2)

然后我们可以容易地对每一组分开来进行构造。

现在考虑有 \(h,l=-1\) 的情况,需要分类进行讨论:

  • \(h=0\)\(l=-1\) 的情况。我们称为环自由元。这种点可以自由决定所属的组,但必须在环上。

  • \(h\gt 0\)\(l=-1\) 的情况。我们称为长度自由元,这种点可以自由决定所在的组,但高度一定。

  • \(h\) \(=-1\)\(l\) 确定的情况,我们称为高度自由元。这种点所在的组确定,但高度可以自由决定。

  • \(h,l=-1\) 的情况。我们称为完全自由元。这种点非常自由啊。

然后我们对环上和环外分开构造:

对于环上,我们考虑对于长度 \(x\)。假设选了 \(y\) 个环。首先 \(xy\) 应该大于等于本组 \(h=0\) 的非自由元个数。并且我们可以看出当 \(y\gt 0\) 时,让其再变大是没有意义的:此时我们需要运用一些环自由元或者自由元或者本组的高度自由元来放在环上。但是这些点在环更少的时候本来就可以自由放置。

不过你注意到有时候虽然本组里没有 \(h=0\) 的非自由元,我们也不能让 \(y=0\)。原因是如果本组内有高度自由元那就必须至少有一个环。然后我们用这些条件就能得出每个长度的环的个数。

但是我们还不能直接确定环的方案。这里面涉及三个量的分配:本组的高度自由元,环自由元,完全自由元。并且他们之间没有什么谁严格优于谁的偏序关系,因此也不能确定分配的优先级别。我们不妨先来考虑环外的情形。

棘手点在于条件 \(2\):根据每一组的非自由元,我们对于每一组都能得到一个约束 \(x_i\),表示高度为 \(1\sim x_i\) 的都必须存在至少一个,并且没有高度为 \(x_i+1\) 或更大的。

而长度自由元在绝大多数时候是用来满足这个约束的:比如本来没有高度为 \(a\) 的点,然后我们调用一个长度自由元过来。但它有时候会帮倒忙:比如有一个 \(h\) 极大的长度自由元,那么它放在一个 \(h\gt x\) 的组里就会造成额外的约束。我们称这种情况是产生了一次非理想情况。

一个很重要的事情是不会有两组产生非理想情况:考虑 \(h\) 最大的那个长度自由元,设其高度为 \(mx\)。然后我们能把所有其它的,产生非理想情况的长度自由元都移动到 \(mx\) 所在的那一组,避免产生其它的非理想情况。

那么我们枚举一个组 \(p\),表示这一组的 \(x\)\(mx\) 需要取较大值。然后现在就不能把一个长度自由元放在 \(x\) 比自身的 \(h\) 小的组内了。此时我们解决了长度自由元会帮倒忙的这个事情。

还是很难办,因为我们依旧涉及三个量的分配:本组的高度自由元,长度自由元,和完全自由元。

这东西看着就非常 flow:建立一个二分图结构,右部有 \(2n\) 个点,前 \(n\) 个代表环上的分配,后 \(n\) 个点代表环外的分配。。左部有 \(mx+n+2\) 个点。前 \(mx\) 个代表的是 \(h=1\sim mx\) 的长度自由元给环外的影响,最后 \(n\) 个代表的是第 \(i\) 组的高度自由元对环/环外的影响。中间还有 \(2\) 个点,一个代表完全自由元,另一个代表环自由元对环的影响。

注意到这个图的右部点只有 \(O(\sqrt n)\) 个点有意义,所以图的边数是 \(O(n\sqrt n)\) 的。那么跑 \(n\) 遍 flow 的复杂度就是 \(O(n^3)\)

实现的时候有些细节与题解不同,因为是模拟时匆忙写的代码。

代码

gym103469K K-onstruction

k-on 导致的。这个题属于那种,玩玩图一乐就行。

首先容易做到 \(3\log\) 的构造:我们假设当前集合内所有正数和为 \(x\)。则选取一个 \(y\gt x\),并且同时加入 \(y,-y\) 即可做到让答案 \(\times 2\)。若我们仅仅加入 \(-x\),就可以让答案 \(+1\)

因此我们可以从 \(1\) 开始,不断地让答案先乘二,然后再选择是否 \(+1\)。这样就可以在 \(3\log\) 的时间内完成构造。

拓展这个想法:我们尝试找出更多的形如 \(\times x+y\) 的操作 \((x,y)\)

我们加入一些数以后,乘法是很容易做到的:原集合和新加入的数若分别是 \(0\) 就达到了相乘的目的。

加法从哪里来?其实是从,那些既有原集合元素也有新添加元素的集合中来。原集合有很多元素,我们不好控制。

那么不妨让此时原集合只有一种选择:比如说正数和大于负数绝对值和,设其为 \(P\),那么我们新添加一些值为 \(P/-P\) 的元素。这样的话,若同时从原集合与新添加元素中选子集,则必定选则原集合内的所有正数。

沿用这个思路,我们初始向集合添加 \(\{2,-1\}\)。然后不妨假设正数大于负数绝对值和,设为 \(P\)。向集合添加 \(P/-P\) 构成的集合,即可得到新的 \((x,y)\) 操作。

得到若干 \((x,y)\) 后直接跑 dp。但是只是用 \(P/-P\) 来构成集合会发现不够优秀。

我们选一个较小的阈值 \(k\),然后枚举出 \(-kP\sim kP\) 构成的集合,得到若干 \((x,y)\) 操作。经实践,选取 \(k=-3\) 且大小不超过 \(10\) 的集合构造出的 \(x,y\) 足够通过此题。

代码

CEOI2019 Dynamic Diameter

直径维护姿势有很多,直接 ddp 固然可行。甚至两遍 dfs 的方法使用 LCT 也能解决本题,但都大材小用了。

还有两个常见方法,一个是两点集直径合并可以用两点集分别的直径合并得出,这个方法看上去不太能支持修改,但似乎也有人使用这个做法通过了?

还有一个方法是欧拉序:我们考虑设欧拉序的一个位置是合法的,当且仅当该位置的元素是第一次出现。

则直径等价于选择 \(x\le z\le y\) 使得 \(x,y\) 都是合法位置,且最大化 \(dep_x+dep_y-2dep_z\)

也就是看成是序列上依次选三个元素出来。则区间内维护 \(t(x,y)\) 表示本来确定到第 \(x\) 个元素,经过这个区间后要求确定到第 \(y\) 个元素的最大权。其中 \(0\le x\le y\le 3\),合并类似矩阵乘法。

并且修改也是容易的:等价于区间加,对 \(t(x,y)\) 的影响就是加上了 \((y-x)v\)

时间复杂度 \(O(n + q\log n)\)

代码

一道无名数据结构题

题意:给出长度为 \(n\) 的数组 \(a\) 和参数 \(m\)。定义 \(f_x(i)=\sum_{j=i}^{j\lt i+m \land j\le n}[a_j\le x]\),有 \(q\) 次询问,每次给定 \(x,l,r\) 询问 \(\min_{i=l}^{r}f_x(i)\)。强制在线,空间 32M。

\(n,q\le 2\times 10^5\)

考虑允许离线的做法:我们按照值把 \(a\) 中的元素和询问混合一起排序。然后扫描到 \(a\) 中位置在 \(p\) 的元素,就让 \((p-m,p]\) 处的 \(f\) 全部 \(+1\)。询问等价于区间最小值。

再考虑在线但不严格要求空间的做法:容易想到使用主席树维护每个时刻的 \(f\) 序列。但是大常数 \(n\log n\) 的空间依旧无法通过。

此时考虑就应该是分块平衡到一个 \(O(n)\) 空间 \(O(q\sqrt n)\) 时间的做法。

考虑分块,并且我们把时间轴也分块。令 \(B=\sqrt n\),我们每隔 \(B\) 次修改,记录一次 \(O(B)\) 级别的信息。然后询问的时候再 \(O(B)\) 查询。称 \(B\) 的倍数的时刻为关键时刻。

首先是散块:注意到这题有一个很好的性质是每个位置的 \(f\),管辖的区间都是等长的。如果我们知道 \(f_x(i)\) 就可以 \(O(1)\) 递推出 \(f_x(i+1)\)。因此,我们对于每个关键时刻,维护每个块的左端点的 \(f\)。一次询问时,首先花费 \(O(B)\) 的代价得到此时,一个块左端点的 \(f\)。然后再花费 \(O(B)\) 的代价,就能算出这个块所有位置的 \(f\)。然后我们解决了散块。

整块的话,注意到每次修改操作都会有整块和散块两种操作。我们分开来考虑。

整块操作的话,我们在关键时刻处,对每一个块记录所有覆盖它的整块操作的数目。每次查询时还有 \(O(B)\) 个整块操作,使用差分可以 \(O(B)\) 地统计出每个块被多少整块操作覆盖。

散块操作的话,注意到一共只有 \(O(n)\) 个散块操作。我们对每个整块维护一个 vector。每当其被散块修改到的时候,就 push_back 进去此时的区间最小值。那么查询的时候我们如果 \(O(\log)\) 二分就是 \(O(B\log)\) 的。考虑还是可以优化:在关键时刻处,记录当前块的 vector 的大小。那么查询的时候,我们找到上一个关键时刻这个块的 vector 的大小。然后从这个位置开始向后扫到最后一个 $\le $ 查询时刻的信息即可。每向后扫一次意味着找到了一个散块操作。而从上一个关键时刻到当前的散块操作数依旧是 \(O(B)\) 的,所以我们又在 \(O(B)\) 的时间内完成了“整块受散块操作的影响”这个部分。

时间复杂度 \(O(n\sqrt n)\),空间复杂度 \(O(n)\)

一道无名构造题

这个题真有点震撼到我了。

题意:给出一颗大小为 \(n\) 的树,和一个 \(k\),你需要构造一个排列,或者环排列。使得相邻两点的距离和加起来恰好为 \(k\),或报告无解。排列 / 环排列分别有 \(50\%\) 的得分。\(n\le 5\times 10^5\)

很喜欢一句话,啊?

我会链的情况吗?我会 \(n\le 30\) 吗?我咋啥都不会?我会 \(2^nn^4\),很牛。

厄,先挑一个好做的,这种题给了排列和环排列,那一般是环排列好做。

下界肯定是 dfs 整棵树,\(2(n-1)\),上界是啥?研究一条边 \((u,fa_u)\)的经过次数发现是 \(\min\{sz_u,n-sz_u\}\)。猜测这个相加就是上界。然后 \(k\) 肯定在环排列的语境下需要是偶数。我们猜测这个范围内的所有偶数都可达。

怎么构造?先定重心为根,这样每条边 \((u,fa_u)\) 的经过次数就是 \(sz_u\) 了。这点很重要。

然后考虑那个上界能如何构造?我们从重心出发,然后每次选择一个点,和上一个点所在子树不同即可。因为是重心所以总能达到,然后每条边也确实被经过了 \(sz\) 次,就顶到了上界。

再考虑缩小上界:就是说把一些同子树的点合并来减少答案。

对于一个非叶子节点 \(u\),我们可以随便选择其子树内一个叶子,然后把这个点设置成:当经过这个叶子的时候顺带捎上这个 \(u\)。这样答案会缩小 \(2dep_u\)。对于叶子节点,考虑 dfs 序相邻的,可以合并在一起经过,答案会缩小 \(2dep_{lca}\)

这些操作之间是互相独立的且都做上的话就等价于在做 dfs,缩小到了 \(2(n-1)\)

并且如果我们有一个让答案减少 \(2a\) 的操作就一定有一个让答案减小 \(2(a-1)\) 的操作。

考虑:不断选出当前剩余点最多的子树(也就是剩余操作最多),然后假设我们要缩减 \(2p\),而这颗子树里还剩余的操作里能缩减最多的是 \(2q\)。那么就一定有一个缩减 \(2\min(p,q)\) 的操作在这颗子树里。如果 \(p\le q\) 直接完事了。否则因为我们是选取了一个最大的操作去用,那么就让这个点的操作数减一,然后还满足上面的一切性质,继续递归即可。

这样我们就完成了对环排列的构造。非常精妙啊。

实现的时候,就是一个非叶子节点的操作就是把他放入叶子节点的集合里。两个叶子节点的操作就是把前一个放入后一个叶子节点的集合里。然后我们先把存活(不在他人集合里)的点拉出来排列一个顺序,然后按顺序遍历存活的点,把他的集合内的数按照 dfn 序排序后顺次输出即可。

再考虑排列,就是环排列去掉了相邻的一对点的距离。此时可以是奇数。且下界为 \(2(n-1)-D\),上界为原本上界 \(-1\),这里 \(D\) 是直径。

\(k\lt 2(n-1)\) 时,考虑构造一个 dfs 序来满足约束:找到树上一对距离为 \(2(n-1)-k\) 的点对 \((x,y)\),以 \(x\) 为根进行 dfs,优先往没有 \(y\) 的子树方向 dfs。除了 \(y\) 以外的点都在 dfs 到的时候加入。最后加入 \(y\)。这样的话,如果是环排列答案依旧是 \(2(n-1)\),现在减去了 \(dis(x,y)\),因此符合要求。

\(k\ge 2(n-1)\) 时考虑基于环排列的构造来调整:假设我们能用环排列构造出一个 \(k\) 的解,可以证明,我们一定能够用排列构造出一个 \(k-1/k-2\) 的解。接下来展示构造手段。

若想构造 \(k-1\) 的排列,我们只需要让环排列上相邻的一对点距离为 \(1\)。考虑我们在环排列的构造时得到了一个长度不定序列 \(c\),表示该序列的位置是重心的哪个儿子的子树内的点。设其属于颜色 \(u\)。则注意到 \(dis(u,rt)=1\),尝试把 \(u\) 放在环排列的第二位:若 \(u\) 属于某个叶子所在的集合,就把该叶子所在的集合放在这个位置(然后集合内按照先序 dfn 序排序);否则把 \(u\) 直接放在这个位置即可。

类似的,若想构造 \(k-2\) 的排列,考虑 \(c\) 序列的任意两个相邻元素一定属于不同的子树。设属于 \(u,v\) 子树,注意到 \(dis(u,v)=2\),尝试把 \(u\) 放在这边集合的最后一个位置(按照后序 dfn 排序,相当于回溯的时候遍历),\(v\) 还是放在第一个位置即可。这样就完成了对排列的构造。

可以做到线性。这个题非常难写,很考验代码能力。

一道无名数据结构题2

挺震撼我的数据结构的,写出来就 2k。

题意:给出常数 \(m\),和一个初始为空的序列 \(a\)。然后有 \(q\) 次操作,强制在线:

  • 往序列末尾追加一个元素 \(x\)
  • 询问当前序列所有长度为 \(x\) 的区间的价值和。对 \(998244353\) 取模。

保证任意时刻序列 \(a\) 的元素互不相同。

区间 \([l,r]\) 的权值定义为前 \(\min\{r-l+1,m\}\) 大的元素的乘积。

\(n\) 为序列长度,保证 \(n\le 10^5,m\le 200,q\le 2\times 10^5\)

啊?

先不考虑前 \(m\) 大,考虑 \(m=1\) 的情况。变成一个单调栈维护的内容。

问题是我们怎么统计答案呢?直接维护一个数组 \(ans_i\) 表示长度为 \(i\) 的区间的答案和?这样完全无法统计。

这是这道题的第一个关键点:我们所能知道的是这样的信息:\((x,y,p,w)\) 表示 \(L\in [x,y]\) 的区间,从 \(R\ge p\) 开始权值被设置成了 \(w\)。还会在 \(R\) 过大的时候知道这个信息失效了。那么对于 \(L\in [x,y]\),我们打上一个标记,时刻为 \(p\) 权值为 \(w\)(失效的时候,打一个权值为 \(-w\) 的标记)。那么 \([L,R]\) 的权值可以表示为:\(L\) 这个位置的所有时刻 \(\le R\) 的标记的权值和。

接下来,考虑一个 \(L:(p,w)\) 的标记放在二维平面 \((L,p)\) 上,则 \(w(L,R)\) 就是 \(x=L\) 这条直线上,\(y\le R\) 的点的权值和。

因此,假设当前序列长度为 \(n\),需要查询长度为 \(len\) 的区间之和。那么就应该是:\((1,len)\rightarrow (n-len+1,n)\) 这条线段下方的所有点的点权之和。

一个点 \((a,b)\) 能被右上角是 \((x,y)\) 的询问涉及到,当且仅当 \(a\le x\)\(b\le y-(x-a)\)

先不考虑前者,后者等价于 \(b-a\le y-x\),所以我们只关注一个点的 \((b-a)\)。这样,在原来的过程里,我们对 \(L\in [x,y]\) 都打上了标记,等价于对一个序列,\([p-R,p-L]\) 的位置上都打上标记。查询就是在查前缀和。

这样过的话会多算 \(a\gt x\) 的,但你注意到 \(a\gt x\),也就是 \(n-len+1\) 的点都满足 \(b-a\le y-x=len-1\),所以只需要把所有 \(a\gt x\) 的点权和减去就行。这部分就是对 \([x,y]\) 区间加,区间求和。

然后我们解决了 \(m=1\) 的情况。对于一般的情况,需要拓展我们的单调栈结构。这是一个比较经典的内容:

维护一个链表,表示说,当我们的后缀拓展到这个位置时,前 \(m\) 大的值发生了改变。当加入一个数 \(w\) 的时候,从后往前遍历链表:

  • \(a_x\gt w\),这样的数次数 \(=m\) 时就停止遍历。因为向前肯定无意义了。
  • 否则,我们将 \(x\) 的计数器 \(+1\)。当计数器 \(=m\) 时删除该链表元素即可。

然后我们类似 \(m=1\) 地那样维护答案即可。唯一问题是我们要更新每个位置代表的后缀的前 \(m\) 大的乘积,也就是要不断维护第 \(m\) 大的数。这个东西使用 pq 维护是 \(nm\log m\) 的,太慢。事实上有更为简单的方法:

加入 \(w\) 后,把涉及到的被影响的位置拉出来: \(p_1\lt p_2\lt ... \lt p_k\)。 首先对于末尾的一段满足 \(n-p_i+1 \le m\) 的,直接把第 \(m\) 大和 \(w\)\(\min\) 即可。然后我们删去这些 \(p\)。接下来对于 \(i\ge 2\)\(p_i\) 处新的第 \(m\) 大就是 \(p_{i-1}\) 处以前的第 \(m\) 大。对于 \(p_1\) 我们需要单独计算:遍历链表后其余的位置,然后暴力计算新的第 \(m\) 大即可,复杂度是关于这一次加入所遍历到的链表节点数量,因此整个复杂度 \(O(nm)。\)

然后转成了一个 \(O(nm)\) 次区间加,\(O(q)\) 次查询区间和的问题,使用分块平衡到 \(O(nm+q\sqrt{n})\)

代码写出来真的很短。

QOJ7771 不是这一道据数构结题

一道比较 ad-hoc 的数据结构题,今天的模拟赛出了弱化版放在了 T1,虽然我没有觉得弱化了多少。还剩五分钟的时候堂堂通过呀!

简化问题,先考虑 \([1,n]\) 的答案。

容易将排序过程抽象为:枚举 \(i:1\rightarrow n\),若后缀 \([i,n]\) 的最大值为 \(a_i\),则跳过,认为是一次无意义操作。否则,挑选出后缀 \([i,n]\) 的所有前缀最大值位置,循环右移一次,然后后缀最大值来到了位置 \(i\)。因此,不妨对每个元素,考虑当其成为后缀最大值时,是否其位于后缀的开头。

题目中大量部分分是针对排列的,考虑此时的情况。

对于一个值为 \(x\) 的元素:我们只关注其它元素和他的大小关系,分为小于和大于两种。称前者为红色,后者为蓝色。特殊的,\(x\) 本身也认为是蓝色的。

结论:当 \(x\) 成为后缀最大值时,其一定位于整个序列里最后一个蓝色位置。

这一点不难理解,首先 \(x\) 后侧的最大值应该会在此时移动到 \(x\) 前面,然后根据循环移位的过程,\(x\) 就会移动到它们的位置,也就是最后一个蓝色位置。

\(x\) 有贡献,当且仅当:这个蓝色位置前有至少一个红色位置。

然后我们得到了一个排列的做法,其可以很快地拓展到区间询问:对于一个 \(a_i\) 满足 \(i\in [L,R]\),若 \([L,i)\) 范围内有一个数小于 \(a_i\) 则一定有贡献。否则,考虑 \(a_i\) 右侧第一个小于 \(a_i\) 的位置 \(j\),再考虑 \(a_j\) 右侧第一个大于 \(a_i\) 的位置 \(k\),当 \(R\ge k\) 时才有贡献。容易离线后使用树状数组在 \(O((n+q)\log n)\) 的时间内解决。

对于非排列的情况,不同点在于一个数值 \(x\) 可能有多次出现。

那么考虑把所有数值 \(\ge x\) 的位置称为红色位置。若共有 \(c\)\(x\),则他们的最终位置(也就是成为后缀最大值时所在的位置)就应该是最后的 \(c\) 个红色位置。一个 \(x\) 有贡献还是当且仅当其前面有至少一个蓝色位置。

拓展到区间询问:对于一个 \(a_i\in [L,R]\),若 \([L,i)\) 范围内有蓝色位置,依旧一定有贡献。否则还是找到右侧第一个蓝色位置 \(a_j\)。考虑根据上面的描述,实际上有贡献等价于:

  • \([i,j)\) 范围内,值为 \(a_i\) 的出现次数,不多于 \((j,R]\) 范围内,值 \(\gt a_i\) 的出现次数。

因此对于一个 \(i\),在 \([L,i)\) 不含蓝色位置时,有贡献的 \(R\) 依旧有单调性。可以二分之。更容易的方法是,按照值降序考虑计算每个 \(a_i\) 的内容,然后可以在树状数组二分之求出边界。总之这部分已经平凡了。

时间复杂度 \(O((n+q)\log n)\)

代码

posted on 2024-02-20 15:22  Cry_For_theMoon  阅读(284)  评论(6编辑  收藏  举报