2024.12

2024.12

SP11414

考虑一个以 \(x\) 为根的子树。如果一个子树为全为黑点,那么必败,有 \(SG(x)=0\)

否则每个白点 \(y\) 都可以作为子状态递归进去,这是有向图游戏的和。由于我们选一个白点变为黑点,相当于删去了 \(x\)\(y\) 上的路径,所以我们将这些问题异或起来。有 \(SG(x)=\text{mex}\{SG _2(y)\}\) ,这里的 \(SG_2(x)=\oplus_{y\in son(x)} SG(y)\)

用 01trie 维护这个过程即可。

P6617

套路题。我们考虑维护一个位置 \(i\) 后面最近的一个满足 \(a(i)+a(j)=w\)\(j\) ,记 \(nxt(i) = j\)

然后我们找一下区间最小的 \(nxt(i)\) 即可,每次修改用 set 维护。

然后细节很多,我们想想怎么降低代码复杂度。我们发现,我们可以扩展 \(nxt(i)\) 的定义。

我们记 \(nxt(i)\) 为位置 \(i\) 后面最近的一个 \(w-a(i)\)\(a(i)\) 容易发现这是不影响答案的。

所以我们用 \(O(V)\) 个 set 维护 \(x\)\(w-x\) 的元素的下标。

然后我们每次在 set 中 lower_bound 或 upper_bound 就不用考虑太多边界了,因为一定能找到 \(i\)

P5369

考虑算一些东西然后乘起来得到答案。

我们先想想一个最大前缀和怎样是对的。我们把它分成两个部分,前半部分满足最大子段和等于最大前缀和,后半部分满足最大前缀和小于 0 。

这样就一定是对的。

我们记 \(f(S)\)\(S\) 的所有排列中满足最大子段和等于最大前缀和的排列数。

再记 \(g(S)\)\(S\) 的最有排列中满足最大前缀和小于 0 的排列数。

答案就是 \(\sum sum(S)\times f(S) \times g(\bar{S})\)

现在我们对 \(f(S)\)\(g(S)\) 计数即可。

  1. \(f(S)\) 计数。

我们考虑再排列的前面加入一个数,下标为 \(x\) 。如果 \(sum(S) \ge 0\) 那么 \(f(S \cup\{x\})=f(S \cup\{x\})+f(S)\)

  1. \(g(S)\) 计数。

和上面类似,在排列中加入一个数,下标为 \(x\) 。如果 \(sum(S\cup \{x\}) <0\) 那么 \(g(S\cup \{x\})=g(S\cup \{x\})+g(S)\)

然后就做完了。

P1917

先初步转化,将空格移到白子处或黑子处。然后黑白染色建出二分图。

我们发现一个状态必胜当且仅当有一条增广路。跑 \(O(k)\) 次匈牙利算法就可以了。

想想为什么是对的。我们发现有一条增广路时这条增广路的边数一定是奇数。所以我们沿着这个增广路一直走就可以获胜。

P10338

我们发现抽奖是按一个顺序抽完所有的奖。用抓阄原理理解一下发现答案是 \(\frac{a(i)}{a(i)+b(i)}\times c\)

我们发现如果 \(c>a(i)+b(i)\) 那么上面的式子没用了。我们可以重构这个位置的箱子。直接暴力用线段树维护是对的。我们发现最多只有 \(O(n+m)\) 个位置的箱子需要修改。因为操作 3 最多只会让一个箱子从不需要修改变为需要修改。

然后我们对于操作 1 找到每个位置暴力修改就可以了。剩下是线段树基本维护操作。

P4547

状压 dp 。先想想期望的线性性。实际上是让我们对完美匹配计数,然后带权算在一起。

把两边都放在状态里,然后计数。貌似只能这样?然后空间开不下,又发现可以用 std::map 和记忆化搜索减少状态(状态肯定不是 \(O(2^{2n})\) ,有一些状态无法对应一个二分图)。

我们考虑三种边怎么办?对于一个状态 \(S\) 我们只能找到一些东西然后并起来。

但是每条边可能会重复出现在一些贡献中,没办法直接算。我们把现在的一些决策集合拆掉。

就是把操作 2 和操作 3 拆成两个操作 1。我们发现,如果先选了边 \(a_1 \to b_1\) 再算边 \(a_2 \to b_2\) 。对于操作 2 少算了 \(\frac 1 4\) ,对于操作 3 多算了 \(\frac 1 4\) 。减掉即可。

这样操作对于状态的贡献是不变的。难点主要在于把期望看成带权计数。

P11149

额。设 \(f(x,k)\) 为以 \(x\) 为根的连通块中另一个颜色是 \(k\) 的方案数。直接写出状态转移方程。

如果 \(a(x)=a(y)\) 那么我们考虑合并两个集合(使用线段树合并)。

\[f(x,k) =\begin{cases} f'(x,k) \times (f(y,k)+f(y,a(x))+1) + f'(x,a(x)) \times f(y,k) & \text{ if } k\ne a(x) \\ f'(x,k) \times (f(y,a(y)+1) & \text{ if } k= a(x) \end{cases} \]

如果 \(a(x)\ne a(y)\) 那么相当于单点修改一个值。

\[f(x,k)=\begin{cases} f'(x,k) & \text{ if } k \ne a(y) \\ f'(x,k) \times (f(y,a(x))+f(y,i)+1) + f'(x,a(x)) \times (f(y,a(x))+f(y,k)) & \text{ if } k = a(y) \end{cases} \]

动态开点 + 线段树合并即可。

P10304

首先要发现一个性质 固定一个点 \(y\) 如果 \(x\) 越靠前那么越无法找到一条路径。这个东西具有可二分性。

我们通过钦定一个东西然后用这个东西在图上做二分/倍增。

先把生成树建出来。我们发现树上的情况是好考虑的。只用维护删去一个点的几级祖先之前 1 和 \(x\) 仍是联通的。我们考虑维护这个东西,记为 \(low(x)\)

考虑一条非树边 \(x \to y\) 对答案的影响。我们发现可以直接贡献 \(low(x)=min(low(x),low(y),low(y \to LCA(x,y)))\)

这个东西可以建反图然后用 topsort 算。

最后考虑答案怎么算,先对生成树倍增处理。现在我们只用维护对于每个 \(dep(x)=k\) 的点有多少个。查询操作用动态开点的权值线段树相当于区间求和。

对于每个点,把子树中的点全部一起算,因为删去了点 \(x\) 后剩下的点不连通。

所以可以 dfs 一遍把子树的线段树先合并出来,然后把 \(\left[ low(x), n \right]\) 的点变为 \(low(x)\) 的。在把当前点插进去,最后对每个查询操作在线段树上区间求和。

P9697

贪心,发现 \(x\)\(y\) 只能是 1 或 2 。可以想到所有 \(\left(2, 2\right)\) 一定最先操作,所有 \(\left(1, 1\right)\) 一定最后操作。只用考虑 \((2, 1)\)\((1, 2)\) ,它们本质等价。

我们现在只考虑 \(x = 1, y = 2\) 的情况,否则交换 \(x,y\) 交换 \(l,r\) 。我们连边 \(l \to r\) 。其中图上的一条路径倒过来一定可以将除了起始位置以外的点都变为 2 。

所有我们用 tarjan 算法处理连通图。特别的, \(\left(2, 2\right)\) 可以处理起点的情况。所以我们从这些点开始遍历,再从剩下没访问过的点进行遍历。

最后把操作反过来即可。

P10144

先考虑不合法的情况,然后套路固定一个端点算区间个数。

推一下式子,发现有三个数一起满足限制才会导致最后结果不合法,设这三个数为 \(r(i - 1), r(i), r(i + 1)\)

我们发现只有 \(a(i - 1) \le a(i) \ge a(i + 1)\) 时,\(T\) 要满足 \(T \ge \max(a(i - 1) + a(i), a(i) + a(i + 1))\)

对称的,当 \(a(i - 1) \ge a(i) \le a(i + 1)\) 时, \(T\) 要满足 \(T \le \max(a(i - 1) + a(i), a(i) + a(i + 1))\)

处理一下区间的交,如果不为空那么有解。用 ST_table + 二分找就好了。

CF1830C

先观察出一个性质,如果两个相交的区间都是合法括号串,那么中间的相交位置也是合法括号串。现在我们需要解决这个序列上所有覆盖情况不同的区间,然后用卡特兰数乘起来。

但是直接做不好做。我们考虑使用随机化算法做这个事情。使用 Xor Hash 差分出每个位置的覆盖情况。然后扔进一个 map 里面,就能知道这个区间的大小。

ARC140D

不会数连通块数,发现每个连通块都有且只有一个环,所以变为数环数。

首先先确认一个计数顺序保证我们不重不漏。我们可以考虑原图中每个连通块的情况,如果它连向了一个有环的连通块,那么他不会产生贡献;如果它和一些树(可能没有)一起构成一个新环,那么有 1 的贡献。每个树的贡献从它连向的连通图中计算。

先考虑原来就有环的连通块,剩下随便连都产生贡献,设有 \(m\) 个 -1, 每个连通块贡献 \(n^m\)

发现每个树只有两个去向:一种是变成一个新环贡献 1 ;另一种是扔掉大脑连向有环的连通图,没有贡献。

把所有树划分为两种情况,一种贡献若干个环,另一种乱连没贡献。

\(f(i, j)\) 表示前 \(i\) 个点连为长度为 \(j\) 的情况数,不难写出:

\[f(i, j) = f(i - 1, j - 1) \times s(i) + f(i - 1, j) \]

然后可以算出答案:

\[\sum_{i = 1} ^ m {dp(m, i) \times n^{m - i} \times (i - 1)!} \]

加起来就好了。

CF418E

这个东西长得就很循环节,我们找找它的循环节。打表容易发现除了第一个以外剩下的奇数行和偶数行都是等价的。

考虑证明这个东西一定是对的。先发现第三行相当于执行了一次离散化。偶数行是在统计前缀出现个数,离散化显然不会改变答案。

所以我们只用维护第二行和第三行就行了。第一行直接在数组上修改即可。

我们每次修改会改变第二行中的一连串后缀,开 \(O(\sqrt n)\) 个关于颜色的前缀桶直接用分块改就好了。第三行就在改第二行的同时修改。

时间和空间复杂度都是 \(O((n + q) \sqrt n)\)

模拟赛 T3

赛时没什么时间想了.jpg

时间很关键。先视黑点和查询点都为关键点。找到一个分割点统计左边的黑点到右边查询点的贡献。最近公共祖先恰好是这个点。到这里可以用 cdq 分治,每次统计答案用虚树就好了。每次相当于查询子树内查询点的个数。虚树点数是 \(O(n)\) 的,所以总的时间复杂度是 \(O(n \log^ 2 n)\)

这个太难写了,用之前的线段树合并维护 dp 类似的思想(NOI 2020 命运)。

对时间用动态开点线段树维护。然后 dfs 每个点向父节点合并。我们还要维护这个区间后面有几次查询。然后算贡献直接乘起来,向下递归时把右子树的信息加起来。时间复杂度是 \(O(n \log n)\)

甚至有根号做法,但是这道题卡常我也不会。就这样吧。

posted @ 2024-12-04 18:10  lichenyu_ac  阅读(27)  评论(0)    收藏  举报