2025.4 训练笔记
Week 1
SD 一轮省集 day1 A
题意:对 \(n\times m\) 的网格每个格子中填 \(01\) 权值,求多少种方案满足每个 \(r\times c\) 的子矩阵中 \(1\) 的个数相同,对 \(mod\) 取模
\(n,m\le 10^9\),\(r,c\le 4\),\(\max(n,m)< mod\le 10^9+7\),\(mod\) 为质数
别问我为什么会做这个
看到对一个网格上面填数要求满足什么条件这种题为啥我都不会做……
默认下标从 \(0\) 开始,限制较为严格,分析性质,将以 \((x, y), (x+1, y), (x, y+1), (x+1, y+1)\) 作为左上角的四个矩形进行加减,可以得出 \(a_{x+r, y+c} = a_{x, y+c} + a_{x+r, y} - a_{x, y}\) 的结论
更进一步的,对网格中的一点重复施加该结论,可以得出 \(a_{x, y} = a_{x\bmod r, y} + a_{x\bmod r, y\bmod c} - a_{x\bmod r, y\bmod c}\) 的结论
所以确定了网格的前 \(r\) 行和前 \(c\) 列的 \(rm+cn-rc\) 个网格后可以确定整个网格,但问题是固定它们后可能没有合法网格
如果后面格子 \((x,y)\) 不合法,说明 \(a_{x,y}<0\) 或 \(a_{x,y}>1\),即 \(a_{x\bmod r, y} = a_{x\bmod r, y\bmod c}=1,a_{x\bmod r, y\bmod c}=0\) 或 \(a_{x\bmod r, y} = a_{x\bmod r, y\bmod c}=0,a_{x\bmod r, y\bmod c}=1\)
写成正向条件就是 \(a_{x\bmod r, y}=a_{x\bmod r, y\bmod c}\) 或 \(a_{x\bmod r, y\bmod c}=a_{x\bmod r, y\bmod c}\),推一下发现对于 \(x<r,y<c\),\(a_{x,y}\) 与第 \(x\) 行所有 \(p\bmod c=y\) 的 \(a_{x,p}\) 相同或与第 \(y\) 列所有 \(p\bmod r=x\) 的 \(a_{p,y}\) 相同
那么枚举 \(r\times c\) 个格子它们分别是和行相同,和列相同,和行列都相同,它们之间几乎互不影响,额外的要求是对于每个 \(\bmod r\) 相同的行,\(\bmod c\) 相同的列 \(1\) 个数相同,这个格子与一行相同时就只对它所在列有限制,反之同理,可以预处理每行每列有几个活动的行列时的贡献,复杂度为 \(O(3^{rc}(r+c))\),卡常可过
实际上把行列都相同放到和行相同这个情况里,即只要求和列相同时行不相同,可以做到 \(O(2^{rc}(r+c))\)
P9288 [ROI 2018] Innophone
把点按 \(x_i\) 排序,从小到大扫描,枚举 \(a=x_i\),则要求选一个 \(b\) 让 \(1\sim i-1\) 中的点产生的贡献最大,把点按 \(y_i\) 排序得到另一个序列,每次插入一个数,求 \(\max_{j=1}^i y_j\times (i-rk_j+1)\)
记 \(v_j=i-rk_j+1\),每次插入一个数时相当于把它前面的 \(v\) 加 \(1\)
发现当某一段 \(v\) 整体 \(+1\) 时,找到当前的最大值 \(v_x\),由于 \(y\) 从小到大则只有后面的数可能反超它,用单调栈求出每个可能成为最大值的数和成为最大值时 \(v\) 整体增加了多少,具体的插入一个数时求出它反超栈顶的时间和栈顶反超栈顶下一个数的时间,如果栈顶反超的更晚说明它不可能是最大值,弹出栈顶
于是就可以分块了,插入数时对前面的块打上增加标记,对当前块暴力重构,查询时对每个块维护指针,指向栈中当前的最大值,标记增加时指针向前移动,重构时指针归零,这样时间复杂度为 \(O(n\sqrt n)\)
继续使用反超的思想,然后考虑这个段不一定要按根号分,把它放到线段树的结构上调整一下,我们得到了 KTT,复杂度 \(O(n\log^2 n)\)
实际上这个操作就是 KTT 模板,但是我不会 KTT(
P10656 [ROI 2017] 学习轨迹 (Day 2)
关键结论是注意到答案一定不劣于全选一个序列,所以至少一个序列选的数的和超过序列一半,意味着这个序列第一次使前缀超过一半的数一定会被选
假设是 \(b\) 中的和超过一半,对 \(a\) 从左到右扫描线,这样 \(b\) 前后两半选到哪就是独立的,线段树上维护每个左端点代表区间在 \(b\) 中前一半最靠前能选到 \(fl_i\),后一半最靠后能选到 \(fr_i\)
\(fl\) 单调不增,\(fr\) 单调不降,每次操作是前缀 chkmin/chkmax,可以用颜色段均摊转化为区间加减,同时维护和与最大值,单调栈维护分界点即可
但为啥要构造方案啊,纯纯增加码量
时间复杂度 \(O(n\log n)\)
CF793G Oleg and chess
显然可转化为行列的最大匹配,但朴素建图边数为 \(O(n^2)\)
扫描线,区间加减,线段树优化建图,每个没有标记的点连向它的左右孩子,每次为了不影响前面的点,像主席树一样,把修改的地方再单独建一个点
这样点数边数均是 \(O(n\log n)\),不过常数较大,图是二分图,跑 dinic 的复杂度是 \(O(m\sqrt{n\log n}\log n)\)
但是无论是用 vector 存图还是用链式前向星,都能有数据把它卡的挺满,数据中只卡了 vector,不懂这个题应该怎么过
Week 2
QOJ4996 Icy Itinerary
看到这种至多一个分界点的题要想插入构造了,发现在分界处两侧总能找到地方插入,不过为了保证 \(1\) 在两端,最后再插入 \(1\),分类讨论当前它和当前的两端有没有连边即可
QOJ7895 Graph Partitioning 2
背包,设 \(f(i,j)\) 表示 \(i\) 子树内和根连通的块有 \(j\) 个点时的方案数(\(0\le j\le k\)),\(j=0\) 表示断开子树和父节点的联系
直接做看起来是 \(O(n^2)\) 的,但感觉它其实有效转移不多,用 unordered_map 存一下值不为 \(0\) 的状态会发现居然过了
分析复杂度,当 \(k\le \sqrt n\) 时,直接做是背包容量有限制的情况,\(O(nk)<O(n\sqrt n)\),当 \(k>\sqrt n\) 时,因为子树大小是固定的,会发现子树内剩余的点数只和子树内取了几个大小为 \(k\) 的有关,而这个的数量是 \(\lfloor\frac n k\rfloor<\sqrt n\) 的,即每个点的状态数不超过 \(\sqrt n\),所以总复杂度是 \(O(n\sqrt n)\)
QOJ3998 The Profiteer
每次避免不了做背包,但背包只能快速插入一个数或撤销一次插入,不能快速合并,无法删除
发现从小到大枚举右端点,它们对应的左端点一定是前缀,且前缀长度单调不降
于是能整体二分,\(solve(l, r,nl,nr)\) 表示区间 \([l,r]\) 为右端点时对应的左端点在 \([nl,nr]\) 内,这里我采用找 \([l,r]\) 中点 \(mid\) 对应的左端点的写法
每次整体二分要保证没有加入的信息一定在区间内,这里就只有 \([l,r]\cup [nl,nr]\) 的物品没有加入,剩下的都以 \(a_i\) 大小加入
找 \(mid\) 的对应时如果还朴素的用二分则复杂度会变成 \(O(nk\log^2 n)\),需要每次保留一些结果,可以用倍增的写法就更直观,从 \(nr\) 开始向前倍增,每次在前面的基础上更新,如果倍增成功,则把新增的 \(b_i\) 去掉,将取 \(a_i\) 的一直加入到那个位置,否则留下倍增的,去掉多加的 \(a_i\),发现加入的数的个数总和是 \(O(len)\) 的,复杂度就是 \(O(nk\log n)\)
往下递归时直接全插入不在它们对应区间的即可,注意 \(r\) 和 \(nl\) 的大小关系不确定,有些边界有细节
2025.4.9 模拟赛
A. three
题意可以变为,给出 \(n\) 个 \(m\) 位的二进制数,需要选取一个子集异或,求出异或出的数的最大权值
考虑使用线性基,仅保留基中的向量,可以把时间复杂度降至 \(O(m2^m)\)
当 \(m\) 比较大时,可行的数非常多,无法逐个枚举,对 \(m\) 个列进行排序,考虑按照 \(b_i\) 从大到小的顺序进行贪心
由于 \(2\sum_{i=1}^n 3^i = 3^{n+1} - 3 < 3^{n+1}\),因此如果能保证高位更优的情况下不用考虑低位
所以直接按位贪心,但如果 \(b_i = x\) 和 \(b_i = -x\) 都存在,有一种特殊情况,就是仅存在一个向量,\(x\) 和 \(-x\) 这两位都是 1,剩下的情况 \(x\) 和 \(-x\) 相互独立,也可以直接贪心
\(x\) 和 \(-x\) 这两位都是 1 时,考虑之前的贪心结果,如果 \(x\) 和 \(-x\) 相同,那么可能可以操作使得答案增加 \(2 \times 3^x\),否则无论是否操作 \(x\) 的贡献都不变,这时候需要把它的那一位主元去掉,剩下的当成一个新的向量传入更低位的线性基中
用 bitset 维护线性基,时间复杂度 \(O\left(\frac{nm^2 + m^3}{w}\right)\)
我好像最后一步不太会处理,写了个搜索,不会分析复杂度,感觉按如果每次分叉后的两种情况只有一种会再次分叉来算,毛估估一下复杂度为 \(O(1.5^{m/2})\)?好像不好卡,跑得挺快(
但我为啥没想到线性基啊?用高斯消元好像很麻烦的推出了一个基本等价的东西,还不是很好继续插入
B. pizza
题解给的是 \(O((n+m)\log^2 n)\) 的复杂度,但我应该会单 \(\log\)?
首先时间中显然只有发生加入人和人到达目标的时间点是重要的,我们考虑时间不断增大,找到下一个关键的时间点,执行操作,让时间增加,如此循环直到结束
还有性质是人加入后相对顺序就不变了,因为速度是一样的;由于每次是把前缀最小值加一,因此除了它原来的前缀最小值不变,如果它还是前缀最小值,那它后面在下一个原来前缀最小值之前与它加一后相同的数会成为新的前缀最小值,否则它不是前缀最小值,这样每次最多去掉一个前缀最小值,而总数量是 \(O(n)\) 的,因此暴力更新变化的前缀最小值只会更新 \(O(n+m)\) 次
维护每个位置的情况在删除和插入时会带来很多问题,因此考虑找支配对,每次要找的是取到时间最短的,只有相邻的前缀最小值和人是有用的,只维护有用的对
用线段树维护每个前缀最小值还剩多长时间被取到,和取到它的人编号,这里注意不是前缀最小值的要设为 \(\infty\)
考虑当前如果加入一个人,则它可能会更新它的前缀最小值取到时间,如果它到前缀最小值的位置没有其它人,则更新
如果要取出一个最小值,则先去掉它,再从后往前考虑它后面新成为前缀最小值的位置和它,同样的每个位置找后面一个人,如果是相邻的就更新,快速找出能成为最小值的位置就再用一棵线段树,维护还不是前缀最小值的数,每次在区间中找最靠后的最小值,然后去掉它
注意此时还要更新取出的最小值前面一个前缀最小值,每次更新时要维护人和位置的双向匹配关系
找到某个位置后面的人可以用 set 存人的相对位置,查询时还原一下位置二分一下,找某个人后面的位置就把合法位置存到 set 里
时间复杂度 \(O((n+m)\log n)\),细节非常多
题解做法
我们考虑按照算法二的顺序,按照 \((a_i, -i)\) 从小到大的顺序,确定每一块披萨是被哪个人得到的。假设被第 \(j\) 个人得到,则要求:
- \(s_j \geq i\)
- \(j\) 会在 \(t_j + v \cdot (s_j - i)\) 的时刻到达,这时候 \(a_i\) 是一个前缀最小值
- \(j\) 是满足上述条件中,最早到达 \(i\) 的,也就是 \(t_j + v \cdot s_j\) 最小
假设 \(a_i\) 在 \(\text{stable}(a_i)\) 的时刻成为最小值,则第二个要求就是 \(t_j + v \cdot (s_j - i) \geq \text{stable}(a_i)\),也就是 \(t_j + v \cdot s_j \geq \text{stable}(a_i) + v \cdot i\)
而 \(\text{stable}(a_i)\) 就是前缀中所有比 \(a_i\) 小的位置被取走的时间最大值,可以用树状数组简单维护
那么现在的问题就是查询 \(s_j \geq i\) 且 \(t_j + v \cdot s_j \geq \text{stable}(a_i) + v \cdot i\) 的 \(j\) 中 \(t_j + v \cdot s_j\) 的最小值,同时还要支持删除已经确定目标的 \(j\)
可以分块做到 \(O((n + m)\sqrt{m \log m})\),也可以用树套树做到 \(O((n + m)\log^2 m)\)。但注意到第一维是一个后缀限制,第二维相当于一个 \(\text{lower_bound}\),因此可以用树状数组套 \(\text{set}\) 来简单维护,代码量和常数都较小
[AGC044C] Strange Dance
这个题很套路,但告诉我们几进制 Trie 都是一样的,这里从低位往高位建,\(S\) 操作打上交换 \(1,2\) 子树的 tag 就行了,\(T\) 操作则把子树 \(0\to 1,1\to 2,2\to 0\),再把原来是 \(2\) 的子树进位,即对这个子树递归操作,复杂度 \(O(n|S|+3^n)\)
P6018 [Ynoi2010] Fusion tree
树上维护发现瓶颈在每次操作需要 \(O(deg)\) 时,可以考虑不维护父节点对应的信息
以 \(1\) 为根,对每个点维护它子节点权值的从低位到高位的 Trie,单点修改 \(x\) 则在 \(x\) 的父亲 \(y\) 的 Trie 中删除一个数再加入一个数,对 \(x\) 邻域 \(+1\) 则用上题的方法,再对父节点单点修改即可,Trie 内维护每层 \(1\) 边的数量,查询则按位求出后异或上父节点权值即可
维护每个点的真实值需要额外记录对每个点邻域加的次数,单点修改直接修改,查询时查它当前的单点值加上父节点被邻域加的次数
时间复杂度 \(O(n\log V)\),注意 Trie 的高位要开够
[AGC020E] Encoding Subsets
这种猜状态数的题到底怎么做啊……
不考虑复杂度的 DP 大家都会,设 \(f(s)\) 表示 \(s\) 的子集的总变化数,\(g(s)\) 表示将 \(s\) 的子集划分为整个循环的方案数
\(g(s)\) 的转移则枚举 \(|s|\) 的因数为循环节长度,发现每个循环节 \(t\) 某一位若能是 \(1\) 则 \(s\) 中这一位对应的所有位置都要是 \(t\),用 \(f(t)\) 转移,注意单个字符的串 \(g(s)=1\)
\(f(s)\) 的转移则枚举 \(s\) 第一个字符所在循环节,用 \(g(t)\times f(s-t)\) 转移
现在问题在于状态数,即不同 \(s\) 的个数
除了原串的区间,新增的 \(s\) 是通过划分循环节类似于取交集的过程产生的,\(T(n)=\sum_{i=1}^n (n-i+1)(1+\sum_{d|i}(i+T(d)))\),原串中串长是 \(i\),它有 \(n-i+1\) 个,每个要枚举它的循环节长度 \(d\),打表发现 \(T(100)=243422222\),可以通过,真的难绷
CF1548E Gregor and the Two Painters
给每个连通块找代表元,对代表元的个数计数,取 \(a_i+b_j\) 最小的格子 \((i,j)\) 为代表元,如果有多个则取 \(i\) 最小的,\(j\) 最小的
对每行,每列找前面第一个小于等于它的 \(pre\),后面第一个小于它的 \(nxt\),讨论一下如果某个格子能走到 \(pre\) 或 \(nxt\) 代表的行列就不行了,进一步发现最优的行走一定先贴着当前的行列,再转弯,因此充要条件是不能到达 \((i,pre_j),(i,nxt_j),(pre_i,j),(nxt_i,j)\) 这四个格子
然后把条件写出来发现是二维偏序,复杂度 \(O(n\log n)\)
[AGC034C] Tests
如果 \(a_i\ge b_i\) 则想让和 \(s\) 尽量大,取 \(r_i\),反之 \(a_i<b_i\) 则取 \(l_i\)
直接做不太好做,考虑二分答案,假设当前 \(\sum_{i=1}^n a_i = x\)
\(a_i\) 取 \(X\) 可以产生 \(r_i(X-b_i)\) 的正贡献,\(a_i\) 取 \(0\) 可以产生 \(l_ib_i\) 的负贡献
最多只有一个 \(a_i\) 不是 \(0\) 或 \(X\),因为若有这样的两个则可以让一个变大,另一个变小,调整使得 \(s\) 更大
枚举这个数的位置 \(i\),可以算出最多有多少 \(a_i\) 取 \(X\),先假设所有的取 \(0\),把调整成 \(X\) 后增加贡献最多的变成 \(X\),这个可以提前排序,讨论一下枚举的位置是否在范围内即可,复杂度 \(O(n\log V)\)
[AGC061C] First Come First Serve
可能会有很多种选择方式对应了一种顺序,考虑让每种不同的顺序只对应一种选择方式
我们规定如果人不改变顺序的情况下能选前面就选前面,这样从前往后考虑,每个人如果能选后面,则说明有人在 \((a_i,b_i)\) 之间
可以暴力 DP,设 \(f_{i,j}\) 表示前 \(i\) 个人,后面要有人时间在 \(j\) 之前的方案数,\(g_{i,j}\) 表示前 \(i\) 个人,后面人无限制,当前最靠后的时间为 \(j\) 的方案数,转移则讨论下一个人能否选后面,以及选前面会不会解除限制等,发现 DP 数组只需支持区间推平为 \(0\),单点修改,区间查询,可以用线段树维护,复杂度 \(O(n\log n)\)
题解还给出了基于容斥的 \(O(n)\) 做法,容斥钦定一些人选后面但不满足有人在 \((a_i,b_i)\) 时间内,发现这样钦定的人对其它人的限制是不交的,方案数可以算,于是就能带上 \(-1\) 的容斥系数 DP
[AGC068A] Circular Distance
计算最远距离 \(\le x\) 的方案数 \(f_x\),差分后能得到最远距离刚好为 \(x\) 的方案数
假设 \(0\) 号点必选,则将最后答案乘以 \(\frac l n\),每个点都可以看作 \(0\) 号点,而这样一种方案会被算 \(n\) 次
那么 \([1,x],[l-x,l-1]\) 内部的点没有限制,只有两部分点之间有限制
将 \([1,x]\) 区间内点看成白点,\([l-x,l-1]\) 内点看成黑点,假设选了 \(i\in [1,x]\),则 \([i+x+1,l+i-x-1]\) 内的点不能选了,这个限制右端点在 \([l-x,l-1]\) 内,设限制区间长度 \(len=l-2x-2\),很神仙的想法是把后一段区间平移到前一段区间上,\(l-x\) 对应 \(1\),\(l-1\) 对应 \(x\),则限制相当于 \([i-len,i]\) 内不能选黑点
问题转化为在长度为 \(x\) 的区间上选点,选一个白点后前面长度为 \(len\) 的区间都不能选黑点,白点黑点一共选 \(n-1\) 个的方案数,特别的最前面的 \(0\) 为白点
枚举一共有 \(i\) 段黑到白的交错,则序列被划分为了 \(2i+1\) 段白黑连续段,最后还可能有一段黑点,先预留空隙,则点有 \({x-i\times len\choose n-1}\) 种选法,再划分连续段,划分好后在除第一段白点的白点段前插入长为 \(len\) 的空隙,考虑在最后加入一个黑点以强制有最后一段黑点,则相当于 \(n+1\) 个数分成 \(2i+2\) 段,有 \(n\choose 2i+1\) 种方法,两个相乘即为方案数
由于 \(i\times len\le x\),枚举 \(i\) 的总量是调和计数,复杂度 \(O(L\log L)\)
[AGC066C] Delete AAB or BAA
贪心不对,基本上只能考虑 DP,DP 的过程可以实现几段不相交的删除区间的拼接,所以这时找出极短的(难以再分割的)能被删空的一整段就能转移
观察这样的段,如果两端都是 A,那若能删空则一定可以从中间某处分隔开,只考虑有一端是 B 的情况,A、B 的个数显然要满足 \(c_A=2c_B\),然后惊人的发现这竟然是充要条件
证明考虑归纳法,假设长度为 \(n\) 的序列开头是 B,在序列中找到两个相邻的 A 满足它们的旁边至少有一个是 B 且不是最开头的,如果旁边的 B 不是序列开头的就把它们三个删除,递归到长度为 \(n-3\),若找不到则说明此时 \(n=3\),序列被删空,归纳基础成立
然后就设 \(f_i\) 表示 \(1\sim i\) 最少剩的字母个数,转移则要么不删第 \(i\) 个,要么删以 \(i\) 结尾的一段,后一种转移的优化是平凡的,复杂度 \(O(n)\)
Week 3
P4426 [HNOI/AHOI2018] 毒瘤
设 \(k=m-n\),则 \(k\le 10\),找出图的生成树后发现非树边不多,可以把非树边所在的点的虚树找出来枚举虚树上点的状态 DP,但这样细节很多
\(k\) 很小还能指向广义串并联图方法,在进行操作后剩下的图中 \(|V|\le 2k,|E|\le 3k\),依然可以暴力枚举点的状态
记录 \(f_{i,0/1}\) 表示点 \(i\) 代表的子图中 \(i\) 不选/选的方案数,每条边记录 \(g_{(u,v),0/1,0/1}\) 表示 \((u,v)\) 这条边代表的子图在 \(u,v\) 分别不选/选时的方案数
删一度点:假设删去 \(x\),此时它连向 \(y\),用边上的和 \(x\) 点的方案数更新 \(f_y\)
缩二度点:枚举中间点是否选择,更新产生的新边方案数
叠合重边:直接把对应的 \(g\) 相乘即可
复杂度 \(O(n\log n+2^{2k})\)
QOJ6355 5
序列等价于多重集,朴素的背包是设 \(f_{x,j,i}\) 表示前 \(x\) 种数,选了 \(i\) 个,和为 \(j\) 能否达成,根据经典结论 \(x\le \sqrt s\),做多重背包,但复杂度最优还是 \(O(n^2\sqrt s)\) 的
往背包里加入若干个 \(1\) 是把 \(j,i\) 各 \(+1\),相当于对斜线赋值,不好做,如果是 \(0\) 就转化为了 \(i\) 的一段区间了
于是将每个数都 \(-1\),答案显然不变,但此时就变成了 \(0\) 的个数 \(\le \frac s 5\)
此时 DP 数组中连续的为 \(1\) 的段长度 \(\ge \frac s 5\),于是段数 \(<\frac{5n}s\),用二进制分组优化多重背包,直接整段维护 DP 数组,每次对于每个 \(j\) 整段转移,然后重构段,复杂度 \(O(\sqrt s\times\log s\times \frac{5n}s\times s)=O(n\sqrt s\log s)\)
QOJ8315 Candy Ads
原来求强连通分量的另一种算法 Kosaraju 也有独特的应用,感觉应用场景很像 Boruvka,都是在图的边数达到 \(O(n^2)\),无法显示建图时使用
Kosaraju 算法的流程是先从当前没被 dfs 到的点开始,对图进行 dfs,在每个点回溯时往序列中插入它,全部处理完后再按序列的倒序开始枚举要dfs 的点,没被访问则 dfs,注意此轮是沿反边,每次 dfs 到的点就在一个强连通分量内,且遍历到强连通分量的顺序是拓扑序正序
这题显然是 2-sat,把每个海报拆成选或不选两个点,直接建图边数会达到 \(O(n^2)\),而且限制是三维偏序,不好用数据结构优化建图
其实我们只需要求出每个强连通分量,于是考虑 Kosaraju 算法的优势,它不需要求出每条边,它只需要每次找到一条可以走的就行了,于是用 bitset 优化,将 \(l,r,xy\) 的限制求出前缀和 bitset,每次找到第一个能走的点是求出对应限制和未访问点的交集,复杂度 \(O(\frac{n^2}w)\)
看提交记录好像有人大力 KD-tree 卡过去了(
P5311 [Ynoi2011] 成都七中
Ynoi 中难得的小清新题
考虑询问点都是根的做法,记录每个点到根路径上的点权最大最小值 \(mx_i,mn_i\),则会对 \(mn_i\le l\le r\le mx_i\) 的询问 \((l,r)\) 产生贡献,如果没有颜色的限制这就是二维数点,有颜色的限制后其实一样,倒着从大到小扫描线,记录每种颜色最小的 \(mn_i\),只在这里有 \(+1\),每次查询前缀和即可,复杂度 \(O(n\log n)\)
询问点有很多,考虑点分治,当前层的根为 \(x\),询问点 \(p\),如果 \(p,x\) 在询问中不连通,则分治下去,否则询问 \(p\) 所在连通块相当于询问 \(x\) 所在连通块,转化为询问点是根的情况,复杂度 \(O(n\log^2 n)\)
2025.4.16 模拟赛
B. CF223E Planar Graph
场上看到计算几何直接跳过了!
有基于平面图欧拉定理的做法,找到环内的面数和边数,用 P3249 [HNOI2016] 矿区 的做法统计即可
官方题解很妙,考虑给每个点 \(1\) 的流量,则流出环的总流量减去环外点流入环的流量就是环上和环内的点数
建立一个极远点,把它当作根和汇点,找出图中的生成树,每个点的流量向根流去,那么流入一个点的流量是它的子树大小,环上每个点接受到的环外流量就是树上边在环外的子树大小之和,流出环的流量就是父边在环外的点的子树大小
而把每个点的所有邻边极角排序,那么环外的一定是一段或两端区间,把边的对应贡献算出,可以提前求出前缀和
复杂度 \(O((m+\sum k)\log n)\)
C. CF331E2 Deja Vu
考虑 \(k>0\) 的做法,发现此时一定恰好一条边 \(u\to v\) 上的数是 \(u,v\),其它边上只有一个数,路径被那条边分成三段,可以 DP
扩展到一般情况,由于长为 \(m\) 的路径只有 \(m-1\) 条边,因此根据抽屉原理一定有一条边 \(u\to v\) 的数中出现了连续的 \(u,v\),然后从这条边开始向两端扩展,每步都是固定的,能得到极小的「合法路径」
要让合法路径能够拼接还需要找到边上的点只缺一个路径端点的路径,这样的路径一定从一条边 \(u\to v\) 其中边上的第一个数是 \(v\) 或最后一个数是 \(u\) 开始或结束,这两种情况分别对应缺开头和缺末尾,依然可以扩展,每步固定,能得到极短的「缺口路径」
上面由于每步固定,且最多扩展 \(2n\) 次,复杂度 \(O(n^3)\)
然后发现最终的合法路径形如缺末尾的缺口路径 \(\to\) 合法路径 \(\to\) 缺开头的缺口路径,其中首尾的缺口路径可能没有,然后还可以拼上一条空边,再重复这个结构
于是枚举路径起点,开始 DP,设 \(f_{i,x,0/1}\) 表示长度为 \(i\) 的路径,末尾点在 \(x\),当前的结构中有/无合法路径,转移则枚举从 \(x\) 开始的下一条极短路径的结尾 \(y\),如果已有合法路径则可以拼接缺开头的缺口路径或空边,否则能拼上缺末尾的缺口路径或合法路径
时间复杂度 \(O(n^4)\)
P7739 [NOI2021] 密码箱
操作很奇怪,显然分子分母时刻互质,由输出格式推测要分开维护分子分母 \(a,b\),维护 \(\dfrac 1 {a_1+\dfrac 1 {a_2+\dots}}\) 这样的形式
发现每次 \(\dfrac a b\to \dfrac 1 {x+\dfrac a b}\),实际上变换是 \(a\to b,b\to bx+a\),可以写成矩阵乘法的形式,\(\begin{bmatrix}0& 1 \\ 1& x\end{bmatrix}\times \begin{bmatrix}a \\ b\end{bmatrix}=\begin{bmatrix}b \\ a+bx\end{bmatrix}\)
然后考虑两个操作对转移矩阵的影响,操作 W 使末尾的矩阵变成 \(\begin{bmatrix}0& 1 \\ 1& x+1\end{bmatrix}\),能看作是 \(\begin{bmatrix}0& 1 \\ 1& x\end{bmatrix}\times\begin{bmatrix}1& 1 \\ 0& 1\end{bmatrix}\)
操作 E 很奇怪,但分两类讨论后发现都能看作是在末尾添加矩阵 \(\begin{bmatrix}0& -1 \\ 1& 2\end{bmatrix}\)
于是直接用平衡树,维护区间翻转/未翻转,区间反转/未反转的矩阵乘积即可,复杂度 \(O(2^3n\log n)\)
[AGC046D] Secret Passage
Too hard.
删除后的字符串可以刻画成三元组 \((i,j,k)\),表示剩下没取的后缀为 \([i+1,n]\),能在后面插入 \(j\) 个 \(0\),\(k\) 个 \(1\)
能得到的串每个对应一个三元组 ,不同三元组对应的那些串不同,而若一个三元组能取到则所有对应它的串都合法,因此要找出哪些三元组合法以及它们对应的串的个数
求个数倒着 DP,每次可以在一个后缀前放下一些数,若 \(s_{i}=0\),则为了不重复让它的前面只能放 \(1\),\(s_i=1\) 同理,状态内记录还剩下多少个 \(0\) 和多少个 \(1\) 可以放
求出能到达的三元组 \((i,j,k)\) 则正着 DP,考虑当前的两个数 \(s_{i+1},s_{i+2}\) 的操作,可以去掉其中一个,也可以从剩下的字符中取出 \(s_{i+1}\oplus 1\) 放在开头,保留它并删除 \(s_{i+1}\)
时间复杂度 \(O(n^3)\)
[AGC044D] Guess the Password
能先问 \(|\Sigma|\) 次,每次问长度为 \(L\) 的一种字符,可以得到每种字符的个数 \(c_i\) 和总长度 \(l\)
编辑距离这个信息范围太宽,关键是考虑能从编辑距离中得到的一种信息是 \(S\) 是否为 \(T\) 的子序列,因为若是当且仅当编辑距离为 \(|T|-|S|\)
从 \(|\Sigma|=2\) 的情况入手,每次尝试在已经确定的串后面加入一种字符,然后把另一种字符剩下的都放在后面,这样问一次就能知道后面这个字符放
置是否正确,总共问 \(2\times \min(c_1,c_2)\) 次即可还原串
那么现在字符集较大,直接分治做这个归并即可,询问次数大概是 \(L \log |\Sigma|+|\Sigma|\),可以通过
[AGC057D] Sum Avoidance
P5327 [ZJOI2019] 语言
考虑对一个点找出答案,把经过它的路径的端点建出虚树,则虚树大小就是它的答案
对每个点执行这样的操作,dfs 的过程中在 \(x\) 处插入以它为一个端点的路径,删除以它为 lca 的路径,维护路径端点按 dfs 序排序后的结果,这个可以用线段树合并或 set 加上启发式合并实现,复杂度为 \(O(n\log^2n/n\log n)\)
P6943 [ICPC 2018 WF] Conquer The World
真不会反悔贪心啊
总数不大,考虑一个一个匹配,每个点有发送或接受的一些权值,贪心匹配是尽量在较深的地方把发送和接受的匹配,然而这并不对,有可能更浅的地方激活了一个离当前点很近点而替换掉原来的匹配
可以反悔贪心,维护发送点,接受点两个堆,正常贪心是把点 \(u\) 以 \(dep_u\) 的权值放到小根堆中,在 \(x\) 处匹配 \(u,v\) 代价是 \(dep_u+dep_v-2dep_x\),反悔操作就是把已经匹配的点 \(u\) 以 \(2dep_x-dep_u\) 的权值放入另一个堆中,取它的意思是取消它的匹配,让当前数匹配 \(u\) 原来匹配的
使用左偏树来合并堆,每个数最多只会反悔一次,所以复杂度为 \(O(n\log n)\)
这题的费用流模型是源点连发送点,接受点连汇点,边上的费用是边权,求最小费用最大流,但我不太会直接从费用流模型推,感觉还是贪心比较自然(?)
Week 4
回去学考+打武汉邀请赛了
P12107 [NWRRC2024] Capybara Cozy Carnival
把切开的地方的所有关键点拿出来离散化,发现选择的方案数只跟边的两端颜色是否相同有关
先断环为链,考虑一条链,固定起点的情况下,终点和起点相同/不同的方案数,可以 DP,设 \(f_{i,0/1}\) 表示 \(i\) 与起点不同/相同的方案数,转移是平凡的,链长度很长,可以矩阵乘法加速,用 \(2\times 2\) 矩阵维护,算一次复杂度 \(O(\log n)\)
然后计算的过程类似于把最外面的环缩成一条边,重复这个过程不断往里缩,直到缩成一条边,可以断开大环建出区间代表的数,但更暴力无脑的做法是发现图为广义串并连图,直接上广义串并连图方法,只做缩二度点和叠合重边,最后一定只剩一条边
设边 \((u,v)\) 的方案数 \(g_{(u,v),0/1}\) 为 \((u,v)\) 同色/不同色的方案数,初始权值用上面方法算出,缩二度点合并分类讨论,细节挺多,注意叠合重边时终点颜色相同要除以 \(k-1\)
时间复杂度 \(O(m(\log m+\log n))\)
P12111 [NWRRC2024] Game of Annihilation
棋子更少的一方不可能赢,棋子一样多一定平局
考虑棋子更多的一方会不会赢,就要看棋子更少的一方是否存在一些棋子在最右端且可以一直往右走不被全部抓到
分析充要条件,把双方棋子按坐标从大到小排序,设棋子更多、更少的前 \(x\) 个棋子分别为 \(a_1,a_2,\dots, a_x\),\(b_1,b_2,\dots, b_x\)
- 如果棋子更多的为先手,若存在 \(x\) 使得 \(\sum_{i=1}^x a_i+1<\sum_{i=1}^x b_i\),则更多的不能赢
- 否则若存在 \(x\) 使得 \(\sum_{i=1}^x a_i<\sum_{i=1}^x b_i\),则更多的不能赢
证明:
考虑归纳,容易发现第一步不管是谁把最右边的棋子往右移动都是不劣的,第一种情况后手移动一步即转化为第二种情况,只证第二种
当 \(x=1\) 时先手在最右边有棋子,这颗棋子只要一直向右就不会被抓到,先手不败
当 \(x>1\) 时找到最小的 \(x\),先手还是把 \(1\sim x\) 中一颗棋子往右移动,后手想封住先手右边的棋,也只能把 \(1\sim x\) 中的一颗棋向右移动,此时若发生抵消则 \(x\gets x-1\),否则不等式仍成立
于是离散化每格棋子个数后缀和,判断即可,第一步的移动方案可以永远是把最右边的棋子往右移动,复杂度 \(O(n\log n)\)

浙公网安备 33010602011771号