做题记录

Two Missing Numbers

Source

Statement

通信题。有一个长度为 \(n\) 的序列 \(a\ (0\le a_i<2^{64})\),满足其中恰好两种数出现次数为 \(1\),其余数字出现次数为 \(2\)。该序列被任意分成两半,分两次喂给你的程序。第一次运行你会得到序列的前半段,你需要输出两个 \(\mathtt{uint64}\);第二次运行你会得到序列的后半段以及第一次输出的两个整数,你需要输出两个 \(\mathtt{uint64}\) 表示那两个出现次数为 \(1\) 的数。

Solution

首先把已经出现 \(2\) 次的数字扔了。现在相当于分两次获得集合 \(S,T\),需要找到 \(S\oplus T\),记这个结果为 \(\{x,y\}\)

考虑神秘哈希,记 \(P=18446744073709551557\),即 \([0,2^{64})\) 中的最大质数。考虑建立一个简单的单射 \(f:[0,2^{64})\cap a\to[0,P)\cap\mathbb{Z}\)。一个简单的想法是 \(f(x)=x\operatorname{xor} M\),其中 \(M\) 是一个自己脸滚键盘得到的常数。因为 \(2^{64}-P=59\),所以可以认为 \(f(a_i)\ge P\) 的概率很小(如果非常不幸出现了,可以再滚一个 \(M\) 提交)。

第一次运行,我们计算 \(\displaystyle a_1=\sum_{x\in S}f(x)\bmod P,\ b_1=\prod_{x\in S}f(x)\bmod P\),第二次运行类似地计算 \(a_2,b_2\)。接下来,依次尝试以下几种情形:

  • \(x\in T\land y\in T\)

    可以得到:

    \[\begin{cases} f(x)+f(y)\equiv a_2-a_1\pmod P \\ f(x)\cdot f(y)\equiv b_2/b_1\pmod P \end{cases} \]

    直接韦达定理解二次方程(需要 Cipolla 开根)。然后映射回去,验证答案。

  • \(x\in T\land y\in S\)

    可以得到:

    \[\begin{cases} f(x)-f(y)\equiv a_2-a_1\pmod P \\ f(x)/f(y)\equiv b_2/b_1\pmod P \end{cases} \]

    是一个一次方程,直接解。然后映射回去,验证答案。

  • \(x\in S\land y\in S\)

    可以得到:

    \[\begin{cases} f(x)+f(y)\equiv a_1-a_2\pmod P \\ f(x)\cdot f(y)\equiv b_1/b_2\pmod P \end{cases} \]

    也是二次方程,直接解。然后映射回去,验证不了,不验证了。

由题目限制可知至少解出一个解。为什么要按照上面的顺序依次尝试呢?因为解这个方程时只知道 \(T\),所以按照验证难度从易到难尝试错误率更小。

Code

Sets May Be Good

Source

Statement

给定 \(n\ (n\le 1000)\) 个点无向图 \(G=(V,E)\)。求导出子图边数为偶数的点集数量。

Solution

考虑一个 \(n\) 元二次多项式 \(\displaystyle F(x_1,\dots,x_n):=\sum_{i\neq j,\ (i,j)\in E}x_ix_j+\sum_{(i,i)\in E}x_i\)。题目相当于求 \(n\) 元组 \((x_1,\dots,x_n)\in \{0,1\}^n\) 使得 \(F(x_1,\dots,x_n)\bmod 2=0\)

直接计算 \(=0\) 的方案有些困难,所以可以考虑计算 \(=0\) 和等于 \(=1\) 的方案数差值。

考虑拆开 \(F\)\(F(x_1,\dots,x_2)=x_1L(x_2,\dots,x_n)+Q(x_2,\dots,x_n)\)

如果 \(L(x_2,\dots,x_n)=1\),那么对于每一种 \(x_2,\dots,x_n\) 方案,\(x_1\) 选与不选恰好对应两种最终 \(F\) 值不同的方案,所以对差值的贡献为 \(0\)

那么只剩下 \(L(x_2,\dots,x_n)=0\) 的情形。注意到 \(L(x_2,\dots,x_n)\) 是一个一次的多项式,所以可以移项得到 \(x_2\) 关于 \(x_3,\dots,x_n\) 的表达式。将这个表达式回带到 \(Q\) 得到 \(Q'(x_3,\dots,x_n)\)。那么我们就是要求 \(Q'(x_3,\dots,x_n)=k\ (k\in\{0,1\})\) 的方案数,这是一个形式相同子问题,递归计算即可。

时间复杂度 \(O(n^3)\)(吐槽数据范围)。

HoMaMaOvO 0:06 过了,天知道怎么做到的。

Text Editor

Source

Statement

Run-twice problem。要求维护数据结构维护可见字符组成的字符串,支持以下操作:

  • \(\texttt{insert}(p,s)\):在位置 \(p\) 插入一个字符串 \(s\)
  • \(\texttt{erase}(l,r)\):移除 \([l,r)\) 子串。
  • \(\texttt{print}(l,r)\):打印 \([l,r)\) 子串。
  • \(\texttt{copy}(l,r)\):将 \([l,r)\) 拷贝至剪贴板。
  • \(\texttt{cut}(l,r)\):将 \([l,r)\) 剪切至剪贴板。
  • \(\texttt{paste}(p)\):在位置 \(p\) 后拷贝剪贴板内容。
  • \(\texttt{undo}\):撤销(不改变剪贴板)。
  • \(\texttt{redo}\):重做(不改变剪贴板)。

第一次运行的最后一个命令是 \(\texttt{serialize}\),你需要输出一个可见字符组成的字符串 \(s\)

然后,第二次运行的第一个命令是 \(\texttt{deserialize}\),你可以知道上次运行输出的 \(s\),然后需要还原上次运行结束时文本编辑器的状态。

操作次数 \(n\le 10^4\),你输出的字符串要求 \(|s|\le 10^7\)

Solution

首先这个东西只能用可持久化 Treap 维护。

于是这题就是让你维护一个可持久化 Treap,并且支持 dump/load。

朴素维护的话用的空间太多了。但是我们发现,一个平衡树的节点可以表示不止一个字符。具体而言,把所有 \(\texttt{insert}\) 操作的字符串拼成一串得到 \(S\),这样每个平衡树节点就可以只维护一对 \(l,r\) 表示该节点的字符串是 \(S[l:r]\)。进行 Treap 分裂时如果恰好裂在某个节点中间,就把这个节点拆成两个。

这样节点数量和需要维护的信息数量都少了很多。\(\texttt{serialize}\) 只需要直接把节点信息压缩打印就行了。应该可以过(吧)。

COW Operations

Source

Statement

给定长度 \(n\) 的字符串 \(s\),字符集为 \(\{\mathtt{C},\mathtt{O},\mathtt{W}\}\)。你可以进行下面的操作之一若干次:

  1. 选择两个相同的相邻字符并删除;
  2. 选择一个字符,将其替换为另外两种字符各一,顺序任意,如 \(\mathtt{COW}\) 可以操作为 \(\mathtt{CWCW}\)

\(q\) 次询问 \(l,r\),求子串 \(s[l:r]\) 是否能够通过上述操作变成 \(\mathtt{C}\)

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

Solution

\(\mathtt{C},\mathtt{O},\mathtt{W}\) 视作 \(1,2,3\)。考虑一个神奇的性质:\(1\operatorname{xor}2=3,\ 2\operatorname{xor}3=1,\ 3\operatorname{xor}1=2\)。所以,两种操作都不会改变整个字符串的异或和。

我们又发现,如果存在相同的相邻字符,可以直接使用操作 \(1\),字符串长度减少 \(2\);否则,先使用操作 \(2\),然后必然可以使用操作 \(1\),字符串长度减少 \(1\)。所以我们必定能够删到只剩一个字符。

因此问题等价于判断 \([l,r]\) 异或和是否为 \(1\)

Not Intersect

Source

Statement

有一个圆,上面逆时针均匀放置着 \(n\) 个点,标号 \(1\sim n\)。你需要画 \(m\) 条线段,要求每个线段必须连接 \(n\) 个点中两个不同的点,且线段之间两两不交。求方案数 \(\bmod 10^9+7\)

\(1\le n\le 10^7,\ 0\le m\le \dfrac{n(n-1)}{2}\)

Solution

我们改为解决这个的问题:有一个 \(n\) 段的圆弧,\((1,n)\) 之间已经连了一条线段,要再连 \(m\) 条不相交的线段,并且边与圆弧不重(也就是不能有 \((i,i+1)\) 的线段)。设这样的答案为 \(f_{n,m}\)

那么原题答案显然是 \(\displaystyle\sum_{i=0}^m f_{n-1,i}\binom{n}{m-i}\),也就是再加入一些与圆弧重的边。

我们考虑求 \(f\) 的生成函数 \(\displaystyle F(x,y)=\sum_{n,m}f_{n,m}x^ny^m\)

考虑边界上最靠外的若干条边,这些边可以看做是将圆弧分成若干段,而每个段内都是形式相同的子问题,如图:

我们先假装把 \((1,n)\) 的边也计入 \(m\),那么有方程:

\[F=y\cdot \dfrac{F^2}{1-F}+x \]

其中,那个分式相当于至少 \(2\)\(F\) 相乘(因为我们不能只分一段),那个 \(x\) 是边界 \(f_{1,0}=1\)

这样解出来是一个二次方程,因此会有两个解,我们需要选择那个 \([x^0]F(x,y)=0\) 的解。

最后,我们要把假装加入的 \((1,n)\) 去除,即,除了 \(x^1\) 之外所有 \(x^i\) 系数都要除以一个 \(y\)

\[F\gets \frac{F-x}{y}+x \]

经过艰苦卓绝的使用 Mathematica 计算,可以得到最终的 \(F\)

\[F(x,y)=\frac{1-x+2xy^2-\sqrt{x^2-2x(2y+1)+1}}{2y(y+1)} \]

然后我们就要计算答案啦。

\[\begin{aligned} ans&=\sum_{i=0}^m [x^{n-1}y^i]F(x,y)\binom{n}{m-i} \\ &=[x^{n-1} y^m] F(x,y)\cdot (y+1)^n \\ &=[x^{n-1} y^m] \frac{1-x+2xy^2-\sqrt{x^2-2x(2y+1)+1}}{2y}\cdot (y+1)^{n-1} \\ &=\frac{1}{2}[x^{n-1}y^{m+1}]\left(1-x+2xy^2-\sqrt{x^2-2x(2y+1)+1}\right)(y+1)^{n-1} \end{aligned} \]

我们发现,分母上的东西都被搞掉了,于是我们只需要展开分子。这里分子是两部分的乘积,我们尝试展开前半部分,这里显然只需要管那个根式:

\[\begin{aligned}[] [x^ny^m] \sqrt{x^2-2x(2y+1)+1}&=[x^ny^m]\sqrt{(1-x)^2-4xy} \\ &=[x^ny^m]\sum_{i=0}^{+\infty} \binom{1/2}{i}(-4xy)^i(1-x)^{1-2i} \\ &=[x^n]\binom{1/2}{i}(-4x)^m(1-x)^{1-2m} \\ &=\binom{1/2}{i}(-4)^m\cdot [x^{n-m}](1-x)^{1-2m} \\ &=\binom{1/2}{i}\binom{1-2m}{n-m}4^m(-1)^n \end{aligned} \]

这里的两个组合数虽然看着十分鬼畜,但是都可以用 \(\dbinom{n}{m}=\dfrac{n^{\underline{m}}}{m!}\) 来拆成下降幂算。至于负数的下降幂,可以每一项提出 \(-1\) 之后转成正数的上升幂。注意到这里隐含了要求 \(m\le n\),所以 \(O(n)\) 预处理后整个式子都可以 \(O(1)\) 记算。

回到上面的式子,既然左边可以 \(O(1)\),右边是一个简单的组合数,那么就可以 \(O(n)\) 计算出 \([x^{n-1}y^{m+1}]\) 的系数,那么整道题就做完啦 ヾ(≧∇≦*)ヾ。

Bracket Xoring

Source

Statement

给定长度为 \(2n\)\(\tt 01\) 字符串 \(s\),你可以进行下面的操作:

  • 选定长度为 \(2n\) 的合法括号串 \(t\),对于每一对匹配的括号 \(t_i,t_j\),将 \(s[i:j]\) 进行 \(\tt 01\) 反转。

使用不超过 \(10\) 次操作将 \(s\) 变为全 \(0\)

\(1\le n\le 2\times 10^5\)

Solution

首先进行一些简单的观察:如果 \(s_1\neq s_{2n}\) 或者 \(\sum_i [s_i=1]\bmod 2\neq 0\),那么无解。

考虑选定一个 \(t\) 之后对 \(s\) 的影响是什么。题目的操作相当于将括号树上奇数层的括号对位置异或 \(1\),而括号树上深度的奇偶性实际上等于左括号下标的奇偶性。于是我们可以把操作等价为:

  • 对于所有 \(1\le i\le 2n\),令 \(s_i\gets s_i\oplus (i\bmod 2)\oplus [t_i=\mathtt{)}]\)

于是我们就可以判断是否能用 \(1\) 次操作完成了。

接下来考虑能否使用 \(2\) 次操作完成,此时显然需要满足 \(s_1=s_{2n}=0\),那么我们的操作变成:

  • 对于所有 \(1\le i\le 2n\),令 \(s_i\gets s_i\oplus [t_{1,i}=\mathtt{)}]\oplus[t_{2,i}=\mathtt{)}]\)

那么对于 \(s_i=0\),相当于限制两个括号串在这个位置相同,而 \(s_i=1\) 则是要求不同。

那么我们可以这样构造:\(t_{1,1}=t_{2,1}=\mathtt{(},t_{1,2n}=t_{2,2n}=\mathtt{)}\),然后对于 \([2,2n-1]\) 中的 \(s_i=0\) 位置,\(t_1,t_2\) 交替填 \(\mathtt{()}\),对于 \(s_i=1\) 的位置,\(t_1\) 交替填 \(\mathtt{()}\)\(t_2\) 交替填 \(\mathtt{)(}\)。可以发现这样子填括号串一定是合法的。

\[\begin{aligned} s&=\mathtt{01011100} \\ t_1&=\texttt{(\color{red}(\color{blue}(\color{red})()\color{blue})\color{black})} \\ t_2&=\texttt{(\color{red})\color{blue}(\color{red}()(\color{blue})\color{black})} \end{aligned} \]

如果 \(s_1=s_{2n}=1\) 的话,我们就随便进行一个操作,使得 \(s_1=s_{2n}=0\),转化成上面的情况。于是我们使用了不超过 \(3\) 次操作完成了这题。

前缀和

Source

Statement

独立地以下面的分布生成 \(n\) 个随机数 \(x_1,\dots,x_n\)

\[P(x=i)=p(1-p)^i\ (i\in\mathbb{Z^+}) \]

\(x_1,\dots,x_n\) 前缀和数组为 \(s_1,\dots,s_n\)。给定 \(n,l,r,p\),求 \(s_i\in[l,r]\)\(i\) 个数期望值。

Solution

假设存在无限长的灯带,每盏灯有 \(p\) 概率是亮的。那么 \(x_i\) 就是第 \(1\) 盏灯的位置,\(s_i\) 就是第 \(i\) 盏灯的位置。所以答案就是 \([l,r]\) 间期望灯的数量,为 \((r-l+1)p\)

Fugitive Frenzy

Source

Statement

在一个 \(n\) 个点的树上进行警察抓小偷。警察先出现在给定的节点 \(s\),然后小偷选择一个节点 \(b\) 出现。接着警察和小偷交替操作,警察先手:警察每一步可以花费 \(1\) 单位时间移动到 \(s\) 的一个相邻节点 \(s'\),小偷每一步可以瞬间移动到节点 \(b'\) 满足路径 \(b\leadsto b'\) 不经过 \(s\),二者都可以选择不动。警察和小偷位于同一个节点时警察抓到小偷。警察不知道小偷的位置,因此他们都会选择概率性的策略,警察希望最小化期望抓捕时间,而小偷希望最大化。

求在双方使用最优策略时期望抓捕时间,答案相对或绝对误差不能超过 \(10^{-6}\)

\(2\le n\le 100\)

Solution

结论:假设警察当前位于 \(u\),那么她一定会以一个概率分布 \(p_u\) 选择一个叶子节点走下去。而小偷也会以一个概率分布 \(q_u\) 瞬移到一个叶子,并且一直等待到警察走到另一个叶子或把自己抓住。

下面是简单证明:

  • 对于警察:

    对于初始情形(位于 \(s\)),小偷可以位于任何地方,警察不知道任何信息。那么警察会一步步走到某个叶子,因为最终的结果都是走到某个叶子,不同的走法本质相同,所以她一定走最短路。而到了叶子后,如果游戏还未结束,那么小偷此时可以瞬移到任何地方,因为所有路径都不经过叶子。于是警察关于小偷的位置不知道任何信息,局面和初始情形一致,所以她仍然会采取相同的策略。

  • 对于小偷:

    小偷希望最大化抓捕时间,因此一定会拖延到叶子再被抓住(如果会被抓住的话)。而随着警察向下走,小偷可以活动的范围有可能被缩小,可行的决策减少,因此小偷一定会在最开始的时候就选定一个叶子蹲着。

于是我们设 \(f_u\) 表示警察位于 \(u\) 时期望还需要多久游戏结束,\(p_{u,v}\) 表示警察处于 \(u\) 时选定 \(v\) 的概率,\(q_{u,v}\) 表示警察处于 \(u\) 时小偷选定 \(v\) 的概率,\(L\) 表示所有叶子的集合,那么:

\[f_u=\sum_{v\in L\backslash\{u\}} p_{u,v}\left(\operatorname{dis}(u,v)+(1-q_{u,v})f_v\right) \]

因为在最优策略下,双方都无法调整 \(p,q\) 使得答案变优,所以我们有:

\[\forall i,j\in L:\frac{\partial p_{u,i}}{\partial f_u}=\frac{\partial p_{u,j}}{\partial f_u},\frac{\partial q_{u,i}}{\partial f_u}=\frac{\partial q_{u,j}}{\partial f_u} \]

而我们知道 \(\dfrac{\partial q_{u,v}}{\partial f_u}=p_{u,v}f_v\),所以 \(p_{u,v}\sim \dfrac{1}{f_v}\),那么:

\[p_{u,v}=\dfrac{f_v^{-1}}{\sum_{w\in L\backslash \{u\}}f_w^{-1}} \]

这个解满足 \(0\le p_{u,v}\le 1,\ \sum_vp_{u,v}=1\) 的约束条件,因此一定会取到。

将这个式子回带到 \(f_u\) 的表达式:

\[\begin{aligned} f_u&=\sum_{v\in L\backslash\{u\}}\frac{f_v^{-1}}{\sum_{w\in L\backslash\{u\}}f_w^{-1}} (\operatorname{dis}(u,v)+(1-q_{u,v})f_v) \\ &=\frac{1}{\sum_{w\in L\backslash\{u\}}f_w^{-1}}\sum_{v\in L\backslash\{u\}}\left(\frac{\operatorname{dis}(u,v)}{f_v}+1-q_{u,v}\right) \\ &=\frac{1}{\sum_{w\in L\backslash\{u\}}f_w^{-1}}\left(|L\backslash\{u\}|-1+\sum_{v\in L\backslash\{u\}}\frac{\operatorname{dis}(u,v)}{f_v}\right) \end{aligned} \]

这个东西和 \(p,q\) 无关,我们可以不断应用上面的式子迭代,求出最后的解。这里迭代次数官方题解没证,我更不会证,那就不证了。复杂度 \(O(n^2+m|L|^2)\),其中 \(m\) 为迭代次数。

Code

Palindrome Addicts

Source

Statement

维护字符串 \(s\)\(q\) 次操作,每次 push back 或者 pop front,然后求 \(s\) 的本质不同回文串个数。

\(1\le q\le 10^6,\ |\Sigma|=26\)

Solution

添加或删除一个字符的时候,本质不同回文串数量只会变化最多 \(1\),所以只需要判断最长回文前/后缀是否只出现一次。

离线,求出所有 push back 操作组成的字符串 \(s\),然后对这个字符串的正反串建回文自动机,分别记作 \(T_1,T_2\)。然后每次相当于查询一个子串 \(s[l:r]\)

  • push back 时:在 \(T_1\) 的 fail 树上倍增跳找到最长回文后缀,然后查询这个后缀最后出现的位置,如果这个位置 \(<l+len-1\) 那么其是第一次出现。每次加入字符时,相当于给 fail 树上一条到根的链上所有节点的最后出现位置 check max,可以用单点修改区间查询的线段树维护。
  • pop front 时:在 \(T_2\) 的 fail 树上倍增跳找到最长回文前缀,接着找到这个回文前缀在 \(T_1\) 上对应的节点,然后查询其最后一次出现位置。要找到 \(T_2\) 节点对应的 \(T_1\) 节点,可以先求出该节点对应的串的最靠前出现位置,然后进一步找到其对应的 \(T_1\) 节点。

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

Code

Hierarchies of Judges

Source

Statement

计数满足以下条件的 \(n\) 个节点的有标号有根树的数量:

  • 每个节点有颜色黑色或白色。
  • 对于每个节点,其本身以及其儿子中,至少一半的节点是白色的。

两棵树被认为不同当且仅当:

  • 对于某个节点,其在两棵树中颜色不同。
  • 对于某个节点,其儿子的集合不同。
  • 对于某个节点,其白色儿子的相对顺序不同。

答案对 \(998244353\) 取模。\(1\le n\le 2\times 10^5\)

Solution

因为把巨大牛迭的 \(O(n \log n)\) 分析成了 \(O(n \log^2 n)\) 导致 ucup 过题 \(-1\),警钟金属键断裂。

考虑 EGF。设根为黑色的 EGF 为 \(F_0(x)\),根为白色的 EGF 为 \(F_1(x)\),则答案是 \(\left[\dfrac{x^n}{n!}\right](F_0(x)+F_1(x))\)

那么列出关于 \(F_0(x),F_1(x)\) 的方程,枚举黑色/白色的儿子数量:

\[\begin{aligned} F_0(x)&=x\sum_{i=0}^\infty\frac{F_1(x)^i}{i!}\sum_{j=0}^{i-1}F_0(x)^i \\ F_1(x)&=x\sum_{i=0}^\infty\frac{F_1(x)^i}{i!}\sum_{j=0}^{i+1}F_0(x)^i \end{aligned} \]

把后面的 \(F_0(x)^i\) 求和用等比数列求和重写,然后整个式子又可以用 \(\exp\) 重写:

\[\begin{aligned} F_0(x)&=x\sum_{i=0}^\infty\frac{F_1(x)^i}{i!}\cdot\frac{F_0(x)^i-1}{F_0(x)-1} \\ &=\frac{x(\exp(F_0(x)F_1(x))-\exp F_1(x))}{F_0(x)-1} \\ F_1(x)&=x\sum_{i=0}^\infty\frac{F_1(x)^i}{i!}\cdot\frac{F_0(x)^{i+2}-1}{F_0(x)-1} \\ &=\frac{x(F_0(x)^2\exp(F_0(x)F_1(x))-\exp F_1(x))}{F_0(x)-1} \\ \end{aligned} \]

那么我们把方程组整理一下,直接用 \(F_0,F_1\) 表示 \(F_0(x),F_1(x)\)

\[\begin{aligned} &G_0(F_0,F_1)=x(\exp(F_0 F_1)-\exp F_1)-F_0(F_0-1) \\ &G_1(F_0,F_1)=x(F_0^2 \exp(F_0 F_1)-\exp F_1)-F_1(F_0-1) \\ \\ &G_0(F_0,F_1)=G_1(F_0,F_1)=0 \end{aligned} \]

考虑使用多元函数的牛顿迭代法。原方程组的 Hessian 矩阵为:

\[H=\begin{bmatrix} \dfrac{\partial G_0}{\partial F_0} & \dfrac{\partial G_0}{\partial F_1} \\ \dfrac{\partial G_1}{\partial F_0} & \dfrac{\partial G_1}{\partial F_1} \end{bmatrix} \]

\(F_0(x),F_1(x)\)\(\bmod x^{n}\) 的结果,\(F_0^*,F_1^*\)\(\bmod x^{2n}\) 的结果,那么:

\[\begin{bmatrix}F_0^*\\ F_1^*\end{bmatrix}=\begin{bmatrix}F_0\\ F_1\end{bmatrix}-H^{-1}\begin{bmatrix}G_0(F_0,F_1)\\ G_1(F_0,F_1)\end{bmatrix} \]

经计算可以验证,\(H\) 总是存在逆,故迭代可以进行。该方法正确性的证明和传统的多项式牛顿迭代是类似的。

在计算过程中,需要进行多项式乘法、多项式求逆、多项式 \(\exp\),因此复杂度 \(T(n)=T\left(\dfrac{n}{2}\right)+O(n\log n)=O(n\log n)\)。常数很大,但是足以通过。

Code

Computational Intelligence

Source

Statement

给定平面上两个线段 \(l_1,l_2\),有两点 \(P\in l_1,\ Q\in l_2\) 均匀随机,求 \(|P-Q|\) 的期望。绝对或相对误差不超过 \(10^{-9}\)\(10^5\) 组数据。

Solution

将线段表示成点向量形式,于是 \(\overrightarrow{OP}=\boldsymbol{u_1}+x \boldsymbol{v_1},\ \overrightarrow{OQ}=\boldsymbol{u_2}+y\boldsymbol{v_2}\)。那么所求的期望可以写成:

\[\int_0^1\int_0^1\sqrt{(A_1x+B_1y+C_1)^2+(A_2x+B_2y+C_2)^2}\ \mathrm{d} x\ \mathrm{d} y \]

开积!但是使用一般的方法,积到下辈子都积不出来,所以我们需要使用多重积分的换元法

\(u\gets A_1 x+B_1 y+C_1,\ v\gets A_2x+B_2 y+C_2\),则原积分转化为:

\[\iint\limits_{(u,v)\in D} \sqrt{u^2+v^2}\left|\frac{\partial(x,y)}{\partial(u,v)}\right|\ \mathrm{d} u\ \mathrm{d}v \]

其中 \(D\) 是变换后的二维区域。

由于 \((u,v)\)\((x,y)\) 的仿射变换,所以其逆变换也是仿射变化,故 Jacobian 行列式是常量,可以先忽略。而原始的积分区域是 \([0,1]\times[0,1]\),所以 \(D\) 显然是一个平行四边形,那么原积分可以拆成若干个下面形式的积分:

\[\begin{aligned} &\int_l^r\int_{au+b}^{cu+d}\sqrt{u^2+v^2}\ \mathrm{d}v\ \mathrm{d}u \\ =&\int_l^r\left.\frac{1}{2}\left(v\sqrt{u^2+v^2}-u^2 \ln \left(\sqrt{u^2+v^2}-v\right)\right)\right|_{au+b}^{cu+d}\ \mathrm{d}u \end{aligned} \]

这个东西事实上可以使用 Mathematica 计算出原函数(相当复杂),于是可以 \(O(1)\) 计算。但使用数值积分应当也可以。

值得注意的是,当 \(A_1B_2-A_2B_1=0\) 时,原积分式中的 Jacobian 行列式不存在(因为此时 \(D=\varnothing\))。所以此时需要使用特殊的还原方法,过程类似,不赘述。

朴素的实现可能会被卡精度。在拆分平行四边形的时候,如果一条线段的 \(\Delta u\) 较小,可能会导致可怕的误差。由于 \(u,v\) 地位对称,可以交换,因此可以选择极差较大的一维作为 \(u\)

Code

Group Division

Source

Statement

给定 \(n_1+n_2\) 个点, \(m\) 条边的无向双连通图,将点集划分成 \(S,T\) 使得 \(|S|=n_1,\ |T|=n_2\)\(S,T\) 内的点连通。构造方案。\(1\le n_1,n_2\le 2000,\ 1\le m\le 5000\)

Solution

先令 \(S=\{\text{*any magical vertex whatever you want*}\}\),然后每次尝试将一个 \(T\) 中的点移动到 \(S\)。这个点需要满足,不是 \(T\) 中的割点并且与 \(S\) 中某个点直接相连。

我们可以通过反证法说明这样的点是始终存在的:如果不存在,说明 \(S\)\(T\) 的边都连向了割点,那么对于一对 \(u\in T,\ v\in S\),所有 \(u\leadsto v\) 的路径都要经过某个割点,这与图的双连通性矛盾。

时间复杂度 \(O(n(n+m))\)

Code

Counting Prefixes

Source

Statement

对于一个长为 \(n\) 的只包含 \(1,-1\) 的序列 \(a\),记其前缀和数组为 \(p\)。现在给出 \(n\) 和排序后的 \(p\),计数有多少个序列 \(a\) 满足条件。\(1\le n\le 5000\)

Solution

把前缀和转化为直线,那么题目相当于告诉我们每一个高度的顶点出现了多少次,记 \(i\) 出现次数为 \(c_i\)

先枚举 \(s\) 表示最终 \(a_i\) 的和,然后令 \(a\gets\{\underbrace{1,1,\cdots ,1}_{s\text{ 个}},\underbrace{-1,-1,\cdots,-1}_{p_n-s\text{ 个}}\}\)。接着,我们每次向一个位置插入 \(\{-1,1\}\),不断插入就可以构造出最终的序列。最终序列与构造方式是一一对应的。

如果我们在一个高度为 \(k\) 的位置后插入,那么高度为 \(k,k-1\) 的点数量均增加 \(1\)。于是我们可以从高到低考虑。对于最高点,初始只有 \(1\) 个,那么就需要插入 \(c_{p_n}-1\) 次,使用插板法计算方案数;对于高度为 \(p_n-1\),我们知道初始时有几个点,也知道上一次的贡献,于是也能够计算;以此类推,就能够求出方案数了。

一次计算是 \(O(n)\) 的,总复杂度 \(O(n^2)\)

Can Bash Save the Day?

Source

KHIN 推荐的。

Statement

给定 \(n\) 个点的树和一个 \(1\sim n\) 的排列 \(p\),进行 \(q\) 次操作:

  1. 给定 \(l,r,x\),求 \(\sum_{i=l}^r\operatorname{dis}(p_i,x)\)
  2. 给定 \(x\),将 \(p_x,p_{x+1}\) 交换。

强制在线。\(1\le n,q\le 2\times 10^5\)

Solution

记排列 \(p\) 的逆排列为 \(p^{-1}\),那么查询相当于只有 \(p^{-1}_u\in[l,r]\)\(u\) 才会产生贡献。

首先考虑树分块。那么我们需要查询的就是块内有贡献点的个数及到界点距离之和,这是一个单点修改区间求和的问题,使用 \(O(1)\) 查询 \(O(\sqrt n)\) 修改的分块维护。总时间复杂度 \(O(n\sqrt n)\)

然后不难发现这个树分块的问题可以继续 reduce,所以可以把树分块改成某种树分治(如点分树),然后每个分治节点用线段树维护单点修改区间求和。时空复杂度均为 \(O(n\log^2 n)\)

在做法上不太有优化空间了,所以考虑题目性质。注意到修改交换的是相邻元素,也就是说 \(p^{-1}\) 的变化量只有 \(1\),而上面的做法都没有依赖于该性质。在每个点分树节点上维护子树内 \(p^{-1}_u\) 的相对顺序,那么每次修改至多交换一对相邻的数,于是可以直接前缀和维护那个区间求和问题。查询时需要在 \(\log\) 个点分树节点上二分 \(l,r\) 对应的 rank,这里可以用类似分散层叠的小技巧优化成 \(O(\log n)\)。总时间复杂度 \(O(n\log n)\)

Code

Wine Factory

Source

Statement

给定长度为 \(n\) 的序列 \(a,b\) 和长度为 \(n-1\) 的序列 \(c\)。按照如下方式建造一个魔法酿酒厂:

初始时,所有 factory 中都是空的。按 \(i=1,2,\dots,n\) 的顺序进行下面的操作:

  1. 向 factory \(i\) 中添加 \(a_i\) 升水;
  2. wizard \(i\) 将 factory \(i\) 中的至多 \(b_i\) 升水变成酒。即,如果有 \(x\) 升水,那么就会有 \(\min\{x,b_i\}\) 升水变成酒;
  3. 如果 \(i<n\),将剩下的水(酒不是水)中的至多 \(c_i\) 升输送给 factory \((i+1)\)。即,如果剩下 \(x\) 升水,那么就会有 \(\min\{x,c_i\}\) 升水输送出去。

现在进行 \(q\) 次操作,每次给定 \(p,x,y,z\),进行修改 \(a_p\gets x,b_p\gets y,c_p\gets z\),然后查询进行一次上述操作会产生多少升酒。

\(1\le n,q\le 5\times 10^5,\ 0\le a_i,b_i\le 10^9,\ 0\le c_i\le 10^{18}\)

Solution

如果写出进入的水量和离开的水量的关系,会有一堆 \(\min,\max\),并不好处理,所以换一种方向思考。

将所有询问离线,维护二元组序列 \((w_i,s_i)\ (1\le i\le q)\) 表示每个询问当前的水量以及总共的酒量,那么我们相当于对于一个区间中的 \(i\),进行如下操作:

  1. \(w_i\gets w_i+a\)
  2. \(s_i\gets s_i+\min\{w_i,b\}\)
  3. \(w_i\gets \max\{0,w_i-b\}\)
  4. \(w_i\gets \min\{w_i,c\}\)

这里所有操作都是可以用 segment tree beats 维护的。需要同时维护 check min 和 check max 操作,细节较多。时间复杂度 \(O((n+q)\log q)\)

Code

Most Different Tree

Source

Statement

给定 \(n\) 个点的有根树 \(G\),记 \(P(G)=\{\operatorname{subtree}(u)\mid 1\le u\le n\}\)。构造另一个 \(n\) 个点的有根树 \(G'\),使得 \(|\{u\mid \exists\ T\in P(G): \operatorname{subtree}(u)\sim T\}|\) 最小化,其中 \(\sim\) 指有根树的同构。

Solution

我们称存在 \(P(G)\) 中元素与其同构的子树为同构存在的。

注意到一个事实,如果 \(T\) 是同构存在的,那么其所有子树都是同构存在的。那么如果我们能够找到一个大小最小的树 \(T\) 使得其不是同构存在的,那么我们可以令 \(G'\)\(T\) 上方接一条长度为 \(n-|T|\) 的链,这样的 \(G'\) 一定满足题目最小化的条件。

找到最小的 \(T\) 可以枚举 \(k=1,2,\dots,n\),暴力搜索所有大小为 \(k\) 的无标号有根树,并树哈希判断其是否是同构存在的。根据抽屉原理,最多搜索 \(n+1\) 个树就能找到一个不是同构存在的树。精细实现搜索过程即可做到复杂度 \(O(n)\)

Code

Finding Satisfactory Solutions

Source

Statement

\(n\) 个顾客与 \(n\) 个物品,每个顾客有一个排列 \(b_i\) 表示他对物品喜好程度的排名。

你有一个物品分配方案的排列 \(a\),表示 \(i\) 号顾客拿到第 \(a_i\) 个物品。

称一个分配方案 \(a\) 是好的,当且仅当不存在一个非空集合 \(S\),使得存在一个分配方案 \(a'\) 满足:

  1. \(\forall\,i \in S, a'_i \in S\)
  2. \(\forall\,i \in S\),第 \(i\) 个顾客相对 \(a_i\) 更喜欢 \(a'_i\)(不要求严格更喜欢 \(a'_i\),即 \(a_i\) 可以等于 \(a'_i\)
  3. \(\exists\,i \in S\),第 \(i\) 个顾相对 \(a_i\) 严格更喜欢 \(a'_i\)

输入物品分配方案的排列 \(a\),请求出有多少种不同的 \(\{b_1,b_2,\cdots,b_n\}\) 排列组 使得分配方案 \(a\) 是好的。

\(n\le 40\)

Solution

最终的分配方案是一个排列,构成若干个环。

首先对于每一个 \(i,j\) 满足 \(b_{i,j}=1\),连边 \(i\to j\)(即每个人的标号连向其最喜欢的物品标号),这样会形成一个基环树森林。不难发现这个基环树森林的环一定是最终排列的环之一。于是我们可以将这些环删掉,对剩下的点递归地操作。

考虑我们过程中连出的所有边组成的图会对应多少种方案。对于边 \(i\to j\)\(j\neq a_i\),则 \(j\)\(b_i\) 中的排名比 \(a_i\) 靠前。于是我们可以这样计算:记点 \(i\) 不记 \(i\to a_i\) 的出度是 \(d_i\),则方案数为:

\[\prod_{i=1}^n d_i!(n-d_i-1)! \]

我们进行状压 DP。设 \(f_S\) 表示集合 \(S\) 中的环构成 DAG 的方案数,那么我们可以枚举 DAG 第一层的环构成的集合 \(T\),并且有下面的转移:

\[f_S=\sum_{T\subset S}(-1)^{|T|+1}f_{S\setminus T}\cdot g(T,S\setminus T) \]

其中 \(g(S,T)\) 表示从集合 \(S\)\(T\) 连边的系数之和,为:

\[g(S,T)=\left(\sum_{i=0}^{|T|}\binom{|T|}{i}i!(n-i-1)!\right)^{|S|}=\left(\frac{n!}{n-|T|}\right)^{|S|} \]

注意这里 \(|S|,|T|\) 指的是点数而不是环数。

然后就是这个 \((-1)^{|T|+1}\) 的容斥系数。咋一看这玩意很没道理,但是我们可以证明这是对的。对于实际上第一层有 \(m\) 个环的方案,计算得到的容斥系数之和 \(S_m\) 可以这样计算:

\[\begin{aligned} S_0&=1 \\ S_m&=\sum_{i=1}^m\binom{m}{i}(-1)^{i+1}S_{m-i} \end{aligned} \]

由数学归纳法易得 \(S_m=1\),容斥系数的正确性得证。

于是我们现在有了 \(O(3^n)\) 的做法了,但是还不够。因为我们的系数都只跟大小有关,所以考虑压缩状态:记状态为每种大小的环各出现几次。那么状态数为下面问题的解:

\[\begin{aligned} &\text{s.t.}& &c_i\ge 0,\ \sum_{i=1}^nc_i\cdot i\le n \\ &\text{maximize}& &\prod_{i=1}^n(c_i+1) \\ \end{aligned} \]

\(n=40\) 时状态数也不超过 \(1500\),十分安全。于是就做完了

Code

Smooth Sailing

Source

Statement

给定 \(n\times m\) 的网格,每个格子可以是 island, ocean 或 underwater volcano。保证 island 格子构成一个连通块。

\(q\) 次询问 \((x,y)\),求出一条 \((x,y)\) 开始的四连通回路,满足其环绕了整个 island,并且最大化路径上任意点到任意 volcano 的最小曼哈顿距离。输出距离。

\(1\le n\cdot m,q\le 3\times 10^5\)

Solution

首先跑出每个格子 \((x,y)\) 到最近的 volcano 的距离 \(d_{x,y}\)

考虑如何判定一个回路是否环绕整个 island。从任意一个 island 格子沿着网格边界引出一条射线,那么回路环绕当且仅当回路经过了这条射线奇数次。

那么我们将一个格子 \((x,y)\) 拆成两个点 \((x,y,0),(x,y,1)\),然后和相邻格子进行连边。如果经过了射线就连向对应 \(0/1\) 取反的点,否则连向 \(0/1\) 相同的点。查询就相当于查最大的 \(k\) 满足加入 \(d_{i,j}\ge k\) 的点后 \((x,y,0)\)\((x,y,1)\) 连通。建 Kruskal 重构树求 LCA 即可。

Minimum Cost Flow²

Source

Statement

给定一张边带权图,求其从 \(1\to n\) 的最小平方费用流,即一条边权为 \(c\),流量为 \(f\) 的边费用是 \(cf^2\),总流量为 \(1\),边没有容量上限。

形式化地说,我们要解决下面的最优化问题:

\[\begin{aligned} &\text{minimize}& &\sum_{e\in E}c_ef_e^2& \\ &\text{s.t.}& &\sum_{e\in\operatorname{out}(1)} f_e-\sum_{\in\operatorname{in}(1)}f_e=1& \\ && &\sum_{e\in\operatorname{out}(u)} f_e-\sum_{\in\operatorname{in}(u)}f_e=0& &,1<u<n& \end{aligned} \]

其中 \(\operatorname{out}(u),\operatorname{in}(u)\) 分别表示进入 \(u\) 和离开 \(u\) 的边。

输出最小费用对 \(998244353\) 取模的结果。\(2\le n\le 100\)

Solution

This is a physics problem.

考虑将 \(f\) 视为电流 \(I\)\(c\) 视为电阻 \(R\)\((1,n)\) 间连接电源,那么这张图形成一个电路。那么总费用就是 \(\sum I^2R\)。学过初中物理的都知道,这个玩意儿就是电路的产热量。根据物理学原理,产热量最小的时候就是电路稳定的时候,即满足基尔霍夫方程组。

设元 \(I_e\) 表示一条边的电流,\(\varphi_u\) 表示一个点的电势,解基尔霍夫方程组即可。

String Journey

Source

Statement

给定长度为 \(n\) 的字符串 \(s\),求最大的 \(k\),满足存在字符串序列 \(t_1,t_2,\dots,t_k\)\(u_1,u_2,\dots,u_{k+1}\),满足 \(t_i\)\(t_{i-1}\) 的真子串且不为空串(\(u_i\) 可以是空串),使得 \(s=u_1t_1u_2t_2\dots u_kt_ku_{k+1}\)

\(1\le n\le 5\times 10^5\)

Solution

首先有下面的几个观察:

Observation 1:我们完全可以认为 \(|t_i|=|t_{i-1}|-1\),如果 \(t_{i-1}\) 更长的话可以将其缩短而不影响答案。

Observation 2:对于一个下标 \(i\),如果 \(r\) 满足 \(s[i:r]\) 可以作为某种方案的 \(t_1\),那么更小的 \(r\) 也可以。

有了上面的观察,我们就有了一种思路:设 \(f_i\) 表示最大的 \(x\) 满足 \(s[i:i+x-1]\) 可以作为某种方案的 \(t_1\)(那么 \(k\) 就是 \(r-i+1\))。那么我们考虑二分,问题转变为判定是否有 \(f_i\ge x\)。假设 \(t_2=s[j:j+x-2]\),那么我们写出来 \(j\) 的约束条件:

\[\begin{cases} j\ge i+x \\ f_j\ge x-1 \\ s[j:j+x-2]\subseteq s[i:i+x-1] \end{cases} \]

对于最后一个条件,我们可以使用后缀数组来刻画,先把条件改写成:

\[\begin{cases} j\ge i+x \\ f_j\ge x-1 \\ \operatorname{lcp}(i,j)\ge x-1 \lor \operatorname{lcp}(i+1,j)\ge x-1\\ \end{cases} \]

于是第三个条件可以刻画成关于 \(\operatorname{rk}(j)\) 的一个区间的限制。我们可以用可持久化线段树维护区间 max 来进行判定。于是有了 \(O(n\log^2 n)\) 的 做法,但是仍然不够高效。

Observation 4\(i+f_i\le (i+1)+f_{i+1}\),即每个 \(i\) 的最远右端点有单调性,这是直观的。

所以可以进行双指针,将判定次数降到 \(O(n)\),并且不再需要可持久化。复杂度 \(O(n\log n)\)

Code

posted @ 2023-10-15 22:21  ExplodingKonjac  阅读(185)  评论(1编辑  收藏  举报