Cry_For_theMoon  

1. Fireflies

考虑用 Dilworth 定理变成求最长反链,发现这个其实就是可重集的 sperner 定理:对于大小为 \(N\) 的可重集,其最长反链是 \(\lfloor \frac{|N|}{2} \rfloor\) 元集合的个数。

本题里因为每个维度的坐标至少是 \(1\),所以其实 \(N = \sum_{i=1}^{n}(p_i-1)\),然后令 \(M = \lfloor \frac{N}{2}\rfloor\)

令一个点的点权是 \(\sum_{i=1}^{n} (x_i-1)\),则答案就是权值为 \(M\) 的点的个数。

为了方便,令 \(M:=M+n\),然后令一个点的点权变为 \(\sum_{i=1}^{n} x_i\),还是问权值为 \(M\) 的点的个数。

如果没有上界的限制,那就是插板一个组合数;有了上界考虑容斥,这样就能做到 \(2^npoly(n)\),此时数据范围基本明示就是折半:设左半部分钦定了集合 \(S\) 不合法,右半部分钦定了集合 \(|T|\) 不合法,两部分的 \(p_i\) 和分别是 \(f(S),g(T)\),则组合在一起的答案就是:\((-1)^{|S|+|T|}\dbinom{(M-1)-(f(S)+g(T))}{n-1}\)

这样的话组合数这里和 \(f(S)+g(T)\) 有关了,我们需要把它拆成只和 \(S/T\) 有关的两部分。注意到根据范德蒙德卷积,它就是:\(\sum_{i=0}^{n-1}\dbinom{(M-1)-f(S)}{i}\times \dbinom{-g(T)}{n-1-i}\)

但是注意,当 \((M-1)\le f(S)+g(T)\) 的时候,虽然范德蒙德卷积的转化依旧成立,但是这个组合数本身的值是有问题的,它应该是 \(0\) 但是根据组合数的严格定义它其实非 \(0\),因为插板法在此时失效了。

因此我们枚举 \(S\) 以后,把 \(T\) 按照 \(g(T)\) 排序,然后可以和 \(S\) 组合的 \(T\) 一定是一段前缀,双指针维护前缀和即可。

比较朴素的实现是 \(O(n^22^{\frac{n}{2}}\) 的,实现的精细一点应该是可以 \(O(n2^{\frac{n}{2}})\) 的。

2. Organizing a Race

这个做法可能不太需要动脑子,不过也容易写绕进去。

感觉这个题挺容易做晕的啊。

考虑怎么判定一条路径是否合法:我们正着跑一遍,如果我们在 \(i\) 处,当在 \(i\) 处加满油然后走完 \(i\rightarrow i+1\) 的路的时候(不在 \(i+1\) 处加油),油箱变负变成了 \(-x\),那么我们就给 \(i\) 的油站加上油。如果我们正着跑到终点,总共的修改量 \(S\) 已经超过了 \(k\) 那肯定没救,否则我们把 \(k-S\) 的油都会加在终点的油站。

枚举 \(L\) 以后,\(S\le k\) 的肯定是一段 \(R\),这个 \(R\) 很容易求,我们可以倒着枚举 \(L\),然后维护一个单调栈。

这样左到右的合法性就基本 over 了,问题在于右到左的合法性。

\(f(i)\) 是从 \(i\) 开到 \(i+1\) 还需要多少油(为负就说明自身有盈余);类似设 \(g(i)\) 是从 \(i+1\) 开到 \(i\) 还需要多少油。

则从 \(l\) 开到 \(r\) 还需要的油就是 \(\sum_{i=l}^{r-1}f(i)\),从 \(r\) 开到 \(l\) 类似是 \(\sum_{i=l}^{r-1}g(i)\)

\(f\) 的前缀和为 \(F\)\(g\) 的前缀和为 \(G\)


现在来考虑 \([1,n]\) 的情况:对于位置 \(i\),当我们倒着开到 \(i\) 的时候,油量会是多少?

除了本来的 \(G(j)\) 以外,我们还要算上额外加的油,而这个油量是 \(k\) 减去必须放在前面的油。

而必须放在 \(\le i-1\) 的车站的油量显然是 \(\max\{0,\max_{j=1}^{i-1}{F(j)}\}\),记这个值为 \(b_j\)

则相当于是要求:\(G(j) - (k-b_j) \le 0\),也就是 \(G(j)+b_j \le k\)


对于 \([L,R]\) 的情况其实也类似:令 \(b_i\)\(\max\{0,\max_{j=L}^{i-1}{F(j)-F(i-1)}\}\),然后相当于是对所有 \(j\in [L,R)\) 都必须有 \(G(j)-G(R) - (k-b_j) \ge 0\),也就是 \(G(j)+b_j \le G(R)+k\)。令 \(c_j = G(j)+b_j\)

依旧考虑扫描线:倒着枚举左端点,则 \(c_j\) 的更新实际上容易用单调栈维护,形式为区间加。

我们相当于找到一个最大的 \(R\),使得 \(L\sim R\) 正着走需要的油量 \(\le k\)(也就是最上面说的可以单调栈出来的),且:\(\max_{i=L}^{R-1}c_i \le k+G(R)\)

\(d_i = \max_{j=1}^{i-1} c_j\)

我们在扫到 \(L\) 的时候,可以把前面的 \(c_i\) 都设成 \(-\infty\),然后假设 \(L\rightarrow x\) 正着走需要的油量 \(\le k\)\(L\rightarrow x+1\) 不满足;则可以把 \(c_{x+1}\) 往后暂时加上 \(\infty\),然后相当于全局查询一个最大的位置 \(R\) 使得 \(d_R \le k+G(R)\),查询完以后在把后面的减去 \(\infty\)


现在抽象成了这样一个问题:有序列 \(d_i = a_i + \max_{j=1}^{i}c_j\),支持对 \(c\) 的区间加减,求全局最大的位置 \(R\) 使得 \(d_R \le k\)

考虑类似楼房重建的套路,区间里维护右子树区间\(\min d\),为了辅助来维护 \(\max a\)\(\max b\),然后 pushup 可以 \(O(\log)\) 完成。

查询的时候,还是考虑在线段树上二分,类似 pushup 的时候引入的 calc,定义 find(pre,x) 表示我们在节点 \(x\) 的区间内寻找,且 \(x\) 以前的前缀最大的 \(c\)\(pre\)

然后依旧讨论 \(pre\)\(lc(x)\)\(\max c\) 谁大,如果 \(pre\) 优秀的情况,比较特殊:我们先去右子树 find,如果找不到,则在左子树管辖的部分其实就是 \(a_i + pre \le k\),由于 \(pre,k\) 已经确定所以其实是在这个节点线段树二分。

在每个 level 只会做一次 seg 二分,所以 \(\log\) 次 seg 二分的复杂度是 \(O(\log^2)\) 的。时间复杂度 \(O(n\log^2)\)

给粉兔磕一个。

记录

3. Rainbow Triples

这个题也好酷。

首先考虑 \(0\) 的分配,设有 \(cnt\) 个,一个显然上界是 \(M=\lfloor \frac{cnt}{2} \rfloor\)

然后前一半的 \(0\) 肯定是作为 \(L\) 出现的,右一半的 \(0\) 肯定是作为 \(R\) 出现的。

我们发现按照中间劈开,左边的非 \(0\) 值,它右边的 \(0\) 永远是能匹配上的;右边的非 \(0\) 值,它左边的 \(0\) 永远是能匹配上的。

所以对于每个非 \(0\)\(x\),如果他在左边,则向左侧的所有 \(0\) 连边;对于右侧,向右侧的所有 \(0\) 连边。

如果没有中心不能相同的限制,则 \(\min(C,M)\) 就是答案,其中 \(C\) 是最大匹配。

带上中心不能相同的限制就再多连几条边:对于每个值 \(x\),连 \(S\rightarrow x\) 容量为 \(1\),然后 \(x\) 向所有出现连边;每个出现,它的连边方式保持不变,每个 \(0\)\(T\) 连边容量为 \(1\)

此时考虑转而求这张图的最小割。

由于我们能割的只有 \(S\) 到每个值以及每个 \(0\)\(T\) 的连边。假设有 \(n\) 个值,我们保留了 \(S\) 集合内的值,为了让图不联通,设 \(S\) 集合内,左部最靠右的位置,前面有 \(f\)\(0\),右部最靠右的位置有 \(g\)\(0\),则此时的割就是 \(f+g+n-|S|\)

此时变成了一个最优化的数据结构问题。我们可以从大往小枚举 \(f\),这样 \(S\) 的可取值就会越来越小,然后对每个 \(v\) 维护此时 \(g\le v\) 的人的个数,当一个数被排除的时候相当于后缀加减,然后每次要求全局最小值。显然可以线段树完成,时间复杂度 \(O(n\log n)\)

记录

4. A Certain Magical Party

嗯分析半天就能分析出来的题,也没用到啥算法数据结构。

我们称一个人给严格小于自己的人讲故事才有收益的是“向下”,反之是“向上”。

如果初始 \(a\) 最小的人还是向下的,那么他没救了,因为他的值不可能变大;所以除非 \(min = max\),否则 \(a\) 最小的人一定都是向上的。

\(min=max\) 的时候答案显然是 \(n!\),判掉就好了。

然后由于 \(a\) 最小的是向上的,则考虑最后一个最小的 \(a\) 加进去一定会变成 \(min+n-1\)。记录这个值为 \(M\)

事实上不需要是“最后一个”,因为我们发现对于每个严格小于 \(M\) 的值而言,最多只有一个人是向上的:如果两个人都是向上的,则第一个变成 \(M\),第二个一定会比 \(M\) 更大。

对于一个值为 \(x\) 的向下的人来说,需要恰好有 \(M-x\) 个严格比他小的在他后面。而如果有多个这样的人,他们的相对顺序是无所谓的,乘一个阶乘就行。

对于一个值为 \(x\) 的向上的人来说,当 \(x\lt M\) 的时候,需要恰好有 \(M-x\) 个小于等于他的在他前面。当 \(M=x\) 的时候,他摆烂放哪里都行,这种情况乘一个排列数特判一下。

所以一般情况下,我们只需要从小往大插入每个数,每个数的位置是唯一确定的。

时间复杂度 \(O(n+v)\),呃呃。

记录

5. A task for substrings

开始 SAM 想半天没想法,最后回归朴素的 ACAM。

前缀匹配是典中典了,建完 ACAM 和 fail 树可以线性维护。

然后 \(F(L,R)\) 就是 \(F(R)-F(L-1)\) 再去掉跨越 \((L-1)-L\) 的匹配数量。

这个看上去很难算啊,但其实不难。

考虑对反串也建出 ACAM,那么令 \(x\) 是正串 \([1,L-1]\) 的节点,令 \(y\) 是反串 \([L,R]\) 的节点,这个可以用 \([L,n]\) 的节点倍增往上跳算出来。

然后问题变成说有多少对 \((p,q)\) 满足 \(p\)\(x\) 的祖先,\(q\)\(y\) 的祖先,且 \(p+q\) 对应的串出现在模式串里。首先这样的 \((p,q)\) 只有 \(O(\sum |S|)\) 对,因此可以把他们挂在 \(p\) 上,然后在第一棵树上 dfs,这样只需要在第二颗树上子树加,单点查询,BIT 即可。

时间复杂度 \(O(\sum |S|\log + |T|+q)\)

记录

6. Find the Array

好题。

考虑这种可以询问集合内任意两点信息的询问,一个套路是我们可以在 \(O(1)\) 次询问内得到:一个点 \(u\) 到一个集合 \(S\) 的所有点的信息 / 两个集合 \(S,T\),之间的所有信息(分别需要 \(2/3\) 次)。

考虑既然能问一个点 \(u\) 到一个集合 \(S\) 的所有点的信息,那就可以考虑找到一个极值然后问出所有点到他的距离。

然后发现我们可以通过二分的方式找到一个最短的前缀 \(x\) 使得 \([1,x]\) 内的极差也是全局极差,那么 \(x\) 这个位置就是极值,但是我们不知道他到底是最大还是最小;不过我们发现如果我们能问出距离,那另外一个最值点的位置也能找到,然后就可以还原整个序列了。

所以现在来考虑有一个位置 \(p\),怎么对所有的 \(i\neq p\) 求出 \(|a_i-a_p|\) 的具体值。

我们可以求出这 \(n-1\) 个值的集合,但是比较困难的是建立对应关系。

其实我们真正能做的是:可以花两次代价确定对于一个集合 \(S\),他们到 \(a_p\) 的距离构成的集合。

然后就可以考虑二进制分组了,对于每个 bit,我们把包含这个 bit 的位置的集合问出来,然后对于每个值,根据他在哪些集合出现,就能知道他的位置。

这样询问次数应该是 \(27\) 次,很酷!

记录

7. Balance Scale

考虑类似计算 DAG 那样把点分层,但显然分层方案不等于答案:如果一个第一层的点和第一层 + 第二层的点都没有连边,则你把他放进第二层还是第一层会被算两次。

还是考虑类似计算 DAG 那样容斥:我们枚举一个集合 \(S\) 是属于第一层的,我们只能计算钦定 \(S\) 是第一层的点,但不能做到强制 \(S\) 恰好为第一层的 点。

具体而言,所有和 \(S\) 有连边的点都不可能再成为第一层的点(因为 \(S\) 内的一个点和它建立了小于关系);剩余的的点里可以选择任意一个集合 \(T\),然后 \(S+T\) 也可以成为第一层的点,容斥系数为 \((-1)^{|T|}\)

不妨枚举 \(T := S+T\),然后考虑哪些 \(S\) 能导出这个 \(T\):根据上面的 \(S\) 导出 \(T\) 的原则,这里的 \(S\) 一定是 \(T\) 中的某些极大连通块构成的。

所以设 \(T\)\(g(T)\) 个连通块,则容斥系数为 \((-1)^{|T|-1}\)(注意 \(S\) 非空的限制)。

然后这个题就在 \(3^n\) 的时间内解决了。

记录

8. Simple Path Counting Problem

很高妙的题。

考虑 \(L=K=1\) 咋做,可以套两条直线 bound 住的路径计数然后做反射容斥,但问题是这里一个点有三种操作,就算没有任何限制的计数似乎也很困难。而且很难对 \(L\times K\) 对统计,所以思考有没有更简单的做法。

考虑把出发点关于 \(m+1\) 对称过去,然后忽略出界的问题,在 \([0,2m+1]\) 这个范围内模意义下游走,然后起点的路径数 - 对称点出发的路径数就是答案。

首先对称过后所有最开始 cross 了 \(m+1\) 的路径都会被去掉(有一条对称的路径在);然后加了一个模意义下的运算,那最开始 cross 到了 \(0\) 的路径,也会有一条对称的路径存在,因此都能被去掉。

然后发现这个东西其实能很好地拓展到 \(L,K \gt 1\) 的情况。

每次转移可以视作乘上了 \((x^{-1} + 1 + x)\) (在 \(\bmod x^{2m+2}-1\) 的情况下)。

那就可以做多项式快速幂了,模 \(x^n-1\) 意义下的乘法就是先乘起来,然后把 \(x^n\) 及其以后的 \(x^i\) 删掉,加到 \(x^{i\bmod n}\) 里。

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

记录

9. Segment-Tree Optimization

考虑第一问,生成 seg 根据经典 trick 可以考虑给序列分段。然后每一个间隔打个 tag 表示是第几次分的,tag 的笛卡尔树就唯一对应一颗 seg(虽然一颗 seg 可能有不同的打 tag 方式)。

那么对于一个 \([L,R]\) 的询问,我们把 \(L-1,R\) 都加入集合 \(S\)。题目里一个 \(d\) 层的线段树最多能做 \(2^{d}-1\) 次分段,所以相当于找到一个最小的 \(d\) 满足 \(2^{d}-1 \ge |S|\),然后第一问做完了。

第二问的话考虑如何分配 \(x = |S| - 2^{d-1} + 1\) 个最后一层的 tag,我们把最后一层的 \(tag\) 记作 \(1\),其余的记作 \(0\)

(从这里也可以看出需要特判 \(d=0\) 的情况,不然会出 \(2^{-1}\) 呃呃,也就是所有都是 \(l=1,r=n\) 的。)

首先因为是标号的笛卡尔树所以 tag 也不是随意打的:两个 \(d\) 层的 tag 之间一定会有一个 \(d-1\) 层的 tag。(其实如果你的 tag 是切割顺序那还是随意打的,但是这里我们打的 tag 不是 \(1\sim n-1\) 的排列,而是它所在的 level,这个不是能随意分配的。)

回到这里也就是两个 \(1\) 不能是连续的。

然后考虑此时如何计算一个 \([l,r]\) 的贡献是多少了。

手玩一下(题目给了一个图,直接玩几个询问区间)就会发现如果 \(l-1\) 被打了 \(0\) 就有 \(2\) 的贡献,对于 \(r\) 也是(由于钦定了相邻不能同是 \(1\),所以左右是独立的)。

那就变成了这样一个问题:你有 \(N\) 个点,要求选择 \(x\) 个不相邻的点出来,每个点被选择了有一个代价,最小化代价。

直接 dp 就是 \(O(N^2)\) 了,注意到套一层 wqs / 反悔贪心 / 模拟费用流啥的就能做到 \(O(N\log N)\)

记录

10. Raffles

考虑一次询问怎么做,把每个奖金池的 \(F(x)\) 差分一下得到 \(f(x)\) 会发现是凸的,但是同时和 \(p,l,x\) 都有关,而且 \(l\) 还同时出现在了分子分母上呃呃。反正现在先不用考虑,直接弄个堆上去就好了。

现在带 \(l\) 的修改,还是 \(+-1\),根据直觉可以猜测调整量 \(O(1)\),我们以 \(l\) 增加说明:首先减少量肯定不会超过 \(1\),因为现在版本的 \(f(x-2)\) 是比修改前的 \(f(x-1)\) 优秀的(分母没变,分子由于 \(l\) 变大了,所以更大);但如何说明增加量也不会很大?其实这里会发现 \(x \le l\) 的限制是有用的,可以根据这个推导出现在的 \(f(x+2)\) 不比以前的 \(f(x+1)\) 优秀。

对于 \(l\) 减少的情况可以类似证明,然后就做到 \(O((n+m)\log n)\) 了。

记录

11. Summer Dichotomy

若两个人的 \([L,R]\) 无交则不能分在一组。

考虑两组总的限制,令 \(a = \max L_1 + \max L_2,b = \min R_1 + \min R_2\),然后相当于 \([a,b]\)\([t,T]\) 必须有交。

也就是 \(a \le T\)\(b\ge t\) 必须同时成立。

因此若 \(L_i + L_j \gt T\) 则它们必须同组;类似地若 \(R_i + R_j \lt t\) 则它们也必须同组。

其实还要考虑空组的情况,也就是对每个人而言 \(L_i \le T\)\(R_i + T \ge t\) 是必须成立的,后一个显然永远成立,前一个不成立的时候显然无解。

上述的限制(包括题目里的 \(m\) 条)全部可以用 seg 加速 2-sat 建图解决。

其实也可以排序后带权 dsu。

记录

12. Line Distance

我日这种题咋想到的。

二分答案以后变成有个圆,然后考虑圆外的所有点两两连线构成的线段,问有多少和圆无交。

这玩意看着就很困难啊,结论是每个点做到圆的两个切点,然后构成一段弧,线段 \(AB\) 和圆不交当且仅当它们的弧相交且不包含 😅?

此时如果是个链则大家都会做了,是个环 gg 了。

发现一个性质:就是这个东西在环上的话,你把弧反转成它的补集,其实答案不会有任何变化。

所以我们可以钦定一个点 \(u\),然后让大家选择弧 - 补集里不跨越 \(u\) 的那个,然后断开成链做一个简单数点。

时间复杂度 \(O(n\log^2)\),关于求切点,由于我们要映射到圆上,所以相当于只用求它的角坐标。

这个的话,考虑令 \(A\)\(O\) 的切点为 \(P\),首先可以求出 \(A\) 的角坐标 \(\alpha\),然后因为角 \(APO\) 是直角,因此角 \(AOP\) 就是 \(\beta = \arccos \frac{OP}{OA}\),然后 \(OP = r\)。这样 \(\alpha +- \beta\) 就是两个切点的角坐标了。

记录

13. Two Avenues

一个边双不会只割一条边,所以要么全割树边要么在一个边双里割两条。

第一种 ez,第二种考虑在 dfs 树上考虑。

若有一条是非树边也很容易,第二条一定是树边且一定是非树边影响范围内,被覆盖次数为 \(1\) 的边。

否则两条都是树边,首先它们肯定是祖先儿子关系不然整个图还会是联通的;然后它们被非树边覆盖的等价类集合必须完全相同,xor hash 一手。

不相同的话可以反证,讨论一下会发现还是连通。

然后考虑每个等价类集合的所有边肯定都是祖先儿子关系,也就是它们的虚树是一条链(所以,不一定是连通的)。

在这条链上随便找两条边割了怎么算贡献?可以容斥:跨越 \(a\) 的,跨越 \(b\) 的,跨越 \(a,b\) 的。

那么我们要算的是:给定 \(u,v\),计算有多少条路径,它的 lca 在 \(u\) 到根链上,它的一个端点在 \(v\) 子树里。

可以直接把每个询问挂在 lca 上,然后 dfs + 主席树。

所以可以 \(|S|^2 \log\) 计算。还能不能更快啊?

注意到有决策单调性,所以变成 \(|S|\log^2\) 了,总复杂度就是 \(n\log^2\) 的(认为 \(n,m\) 同阶)。

记录

这个真的跑的好慢,不会 1log 做法...

14. Trains and Airplanes

呃呃场切了但是没完全场切。

第一段路程是莫得代价的,然后上面的每段颜色,从 \(x\) 出发会在 \([L,R)\) 这段属于它,起点不同会使得 \([L,R)\) 右移若干个长度,然后导致 \([L,R)\)\(\bmod t=0\) 的点数量变化。

然后我们不妨认为右移长度 \(len\) 是在 \([0,t)\) 之间的,在这个范围内 \([L,R)\) 内的 check 次数变化是 \(O(1)\) 的,且只有三段不同的变化。

因此一共也只有 \(O(\Sigma)\) 个本质不同的段,我们现在要做的就是,查询:一个点 \(u\) 的子树内,考虑和它同种颜色的连通块,是否有一个点 \(x\) 满足 \((dep_x-dep_u)\bmod t\) 在区间 \([L,R]\) 内,这个询问是可以离线下来的。查询次数是 \(O(q\Sigma)\) 次。

可以把所有的 \(dep\) 和询问端点离散化一下,然后离线以后,查询子树内信息,可以 dfs 的时候:进入 \(u\) 前后都 query 一次作差,用 bit 维护,这样时间复杂度是小常数的 \(O(q\Sigma\log)\),可以通过。

注意 check 次数 * 罚款加起来可能爆 long long,所以要特判一下,太大了就直接把代价改成买护照。

记录

15. New Year Running

这个题,也是重量级呃呃。

有个暴力的做法就是枚举一个一个路径交点和两个人的方向,然后 exgcd 解一个同余方程组(就两个方程其实),这样是 \(n^2 \log n\)

那这个东西就感觉必须要把树上路径交算出来的,然后就感觉在一条链上移动其实方程组的变化有规律的,这样一个想法。

首先求树上路径交就是个很容易写挂的东西,有一个相对容易的做法:假设两条路径的 lca 分别是 \(p_1,p_2\),且 \(dep_{p_1} \ge dep_{p_2}\):若 \(p_1\) 不在第二条路径上则无解,否则把四个 LCA 去掉最浅的两个,剩下的两个就是路径的端点。可以 \(O(1)\) 但常数贼大。

求出来以后讨论一下,有两种:两个人同一个方向(也就是支流向干流汇合那样的形式),这种情况只会出现在路径交的端点,有 \(O(1)\) 个方程组解一下就好了。

其实更多的还是,两个人相遇的情况,这个是难点。

在一条链上移动,则相当于解 \(len+1\) 个这样的方程组:

\[\begin{cases} x\equiv a_1 - i \pmod {p_1} \\ x\equiv a_2 + i \pmod {p_2} \end{cases} \]

其中 \(i \in [0,len]\)

如果只有一个方程组大家都会解,先把 \(x\) 表示成 \(k_1p_1 + (a_1 + i)\) 的形式,然后就等价于解 \(k_1p_1 + k_2p_2 = (a_2-a_1) + 2i\)

注意到 \(p_1,p_2\) 始终不变,所以可以先解出 \(p_1 x + p_2 y = \gcd(p_1,p_2)\) 的解,令 \(g = \gcd(p_1,p_2),\Delta = a_2-a_1\),则 \(\Delta + 2i\) 必须是 \(g\) 的倍数才行。

两个相邻的是 \(g\) 的倍数的 \(\Delta + 2i\) 之间,\(i\) 的差值可以算出来(如果 \(g\) 是偶数那就是 \(\frac{g}{2}\) 否则就是 \(g\))。

那么每次 \(k_1\) 的增加量也能算出来,然后套到第一个方程里 \(k_1x + a_1 - i\) 也能表示出来了。

总而言之最后可以抽象成这样一个问题:求 \(kx + b \pmod {M=lcm(p_1,p_2)}\),的最小值,其中 \(x \in [0,N]\)

判掉 \(k=0\),然后可以用 \((\min,+)\) 矩阵的万能欧几里得来做。

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

记录

16. Subtree Reversi

很少自己做出来 ARC F,虽然调了半天......

首先把节点按照深度奇偶性分层(根节点深度为 \(0\)),所有深度是偶数的,放下它,最后一定得到它;所有深度是奇数的,放下它,一定是让对面得到它。

我们考虑所有叶子,分为偶数叶子和奇数叶子,考虑两人优先取走所有的偶数叶子。现在这棵树只有奇数叶子了。

注意到:如果我们取走了一个奇数叶子,然后它的父亲变成了叶子,则两人都不会希望自己这样做,因为这样的话两个点都归对面了。

然后一个偶数点如果有多个叶子,叶子之间没有任何区别,所以我们保留其中一个叶子作为关键点,然后剩下的奇数点轮流取走就好了。

然后注意到现在先手的人非常小丑:他只能取一个奇数叶子,而且取完以后对面立马把多出来那个偶数叶子(也就是父亲)取了,然后先手又要来一遍。

那么我们有一个猜测的做法:就是优先取偶数 -> 非关键点的奇数叶子 -> 关键点。

当然肯定不会这么简单,因为关键点并不是随便取的。

如果我们把那个偶数叶子取完以后,他的父亲又变成了奇数叶子,则这个时候,先手取走这个奇数叶子,后手傻眼了,小丑变成了后手。

因此:若一个奇数点不是关键点,则我们称它为特殊点;先手会选一个 size 最小的特殊点,然后把里面的关键点都取走(这个时候这些点都是归给对方的),然后先后手就互换了,这下看着就很对了。

这个咋维护啊?

我这里维护的方法维护一个集合,如果有祖先后代关系就保留深度大的那个,然后每次取出 size 最小的特殊点 \(u\),然后 \(fa_u\) 的儿子里删掉 \(u\);如果 \(fa_u\) 的儿子集合此时大小为 \(1\),令它为 \(p\),则首先 \(p\) 本来不是关键点,现在是了,然后如果 \(p\) 本来是特殊点,那么就向上找到最近的特殊点 \(p'\),然后把 \(p\) 删除,尝试加入 \(p'\)(如果 \(p'\) 的子树内还有没取出的特殊点就不加入)。

这个过程可能需要两个 bit 维护,一个维护真实的 size,一个维护子树内是否有加入集合的特殊点。

时间复杂度 \(O(n\log n)\),实现起来很繁琐。

好像正解这块维护方式很简洁,马上学习一下。

UPD:摆了。

记录

17. Montage

又自己搞出来个 F 啊,这次很顺,不过这个 F 确实相对简单。

限制就是如果左上右下两个人是 \(1\),那么右上左下的两个人也是 \(1\)

所以从下往上一行一行填,假设第 \(i+1\) 行填 \(1\) 的位置集合是 \(S\),我们认为 \(S\) 非空,然后对于第 \(i\) 行,有一些约束:

  • 要么 \(i\) 行跳过,也就是不填任何数。

  • 否则设 \(S\) 的最大值是 \(a\),所有 \(\le a\) 且未出现在 \(S\) 中的位置,第 \(i\) 行一定不能在填;考虑设第 \(i\) 行填 \(1\) 的位置是 \(T\),则 \(T\)\(S\) 的交集一定是 \(S\) 里最大的若干个;然后我们考虑 \(T\)\(\gt a\) 的位置,这部分我们可以任意选择。

注意到 \(n,m\le 400\),这个时候就可以考虑比较暴力的 dp :设 \(f(i,j,k)\) 表示填完了 \(i\sim n\) 行,最靠上的 \(S\) 非空的一行,它的最大值(也就是 \(a\))在位置 \(j\),然后这个 \(S\) 的大小为 \(k\)。转移比较 trivial,多搞几个前缀和优化就行了。

时间复杂度 \(O(nm^2)\)

记录

18. Make Q

考虑枚举环上伸出去那个点 \(u\),然后找到包括 \(u\) 的最小环 \(R\);如果伸出去的 \(v\) 不在 \(R\) 上很简单,在 \(R\) 上的话,如果 \(v\) 不和 \(a\) 直接相连,你会发现总有比这个 \((u,v) + R\) 更优秀的 Q(因为这个结构自身可以去掉若干条边变成一个 Q)。

所以只用 \(R\) 考虑和 \(u\) 相邻的两个点作为 \(v\) 的情况,ban 掉一个点然后再求 \(u\) 所在的最小环即可,设找一次最小环的复杂度是 \(T(n)\) 则复杂度是 \(nT(n)\) 的。

然后考虑找无向图上包含一个点的最小环(有向图上可以直接 dij 一次 \(O(m\log)\),但是无向图会出现让一条边来回走的情况)。

这个目前我知道两种做法:

  • 考虑删去点 \(u\),然后给每个点带权 \(e(u,v)\),则相当于找一对 \((x,y)\) 使得 \(dis(x,y)+e(x)+e(y)\) 最大。这个可以二进制分组跑 \(\log\) 次多源 dij 解决。设跑一次 dij 的时间是 \(D(n,m)\) 那复杂度是 \(D(n,m)\log\)

  • 考虑以 \(u\) 为根建立最短路树,然后设 \(L_x\) 表示哪个 \(u\) 的儿子是 \(x\) 的祖先(如果 \(x=u\)\(L_u=u\))。然后考虑一条非树边 \((x,y)\),若 \(L_x\neq L_y\),则 \(u\rightarrow x\rightarrow y\rightarrow u\) 就构成了一个环。这样时间复杂度是 \(D(n,m)+n+m\) 的。

记录

19. Median Mountain Range

很好的题,但是模拟没做出来感觉太菜了。

不难发现如果有两个相邻的相同的数,我们可以在中间把他们两个人断开,因为这两个人永远不会变了。

然后就考虑一个 01010101 这样的交错段的变化,会发现:如果是奇数,则最后大家会变的一样;如果是偶数,则左半边和最左边的一致,右半边和最右边的一致。

考虑一般情况并没有什么良好性质,考虑用二分转化成 01 序列:也就是对每个位置,我们二分他是否 \(\ge x\),然后把 \(\lt x\) 的设成 \(0\)\(\ge x\) 的设成 \(1\)

然后我们发现只关注两边第一个 \(00/11\) 的位置。此时可以先在值域上扫描线 + 主席树维护,每次二分 + 主席树查询,这样是 \(\log^2\),显然非常不优秀。

考虑按照值域反向扫描线,这样设每次有 \(O(c)\) 个位置产生了修改,那么只有 \(O(c)\) 个区间发生了变化,其余的区间都是在 \(x\) 更大的时候存在过的;而对于每个位置,他肯定只关注最大的能让他变成 \(1\)\(x\),所以我们只用对这 \(O(c)\) 个区间内的点 chkmax,离线下来打个差分标记就行了。其实也可以用 dsu 取出区间所有没被更新的点更新。

时间复杂度 \(O(n\log n)\),其实代码不是很长。

记录

20. Many Increasing Problems

1100 分的 ARC F 还是太难了,感觉如果不是撞 CNOI 原的话这个题至少是个银牌 diff(怎么场上过的全是 CN 人啊)。

先来解决给定序列的情况:这是经典 slope trick,考虑 slope trick 的过程,我们只关注最右边一条线段的截距(这条线段的斜率永远是 \(0\)),当加入一个 \(a\) 的时候,会往 pq 里扔两个 \(a\),然后此时右边线段的斜率变成了 \(1\),且斜率减少了 \(a\),所以我们弹出最大点,然后把 pq 里最大的位置弹出并且修改斜率即可。总而言之:

  • 当加入一个 \(a\) 的时候,我们令答案减去 \(a\),然后往堆里塞入两个 \(a\),然后取出最大的元素 \(x\) 弹出,并且把答案加上 \(x\)

这就是原问题的答案。

考虑维护堆显然是困难的,但维护一个 \(0/1\) 序列的堆情况是容易的,所以这启发我们转成 \(0/1\) 序列来考虑。

每次都考虑弹出的数比较困难,注意到所有弹出的数,所有的 \(a\) 的和,以及最后堆剩下的数的和三者之间存在等量关系;所以我们只需要计算所有 \(a\) 的和,以及堆剩下的数的和即可。第一个问题很容易,第二个问题是重点。

此时我们容易转成 \(0/1\) 序列来考虑:对每个 \(x\in [0,m)\),只要最后序列里有一个剩下的数 \(\gt x\),他就产生 \(1\) 的贡献,也就是把 \(\le x\) 的看作 \(0\)\(\gt x\) 的看作 \(1\)

\(dp_{i,j}\) 表示确定了 \(1\sim i\)\(0/1\) 情况,剩下 \(j\)\(1\) 在堆中,转移很容易:\(dp_{i,j}\)\(x\) 种方案转移到 \(dp_{i,\max\{j-1,0\}}\),有 \(m-j\) 种方案转移到 \(dp_{i,j+1}\)

这样,我们可以 \(O(n^2)\) 对于一个 \(x\) 计算答案,我们要对所有的 \(x\in [0,m)\) 都计算答案并求和,因此导出一个 \(O(n^2m)\) 的做法。

更进一步,发现 \(dp_n\) 是关于 \(x\)\(O(n)\) 次多项式,所以只需要对 \(O(n)\)\(x\) 求就好了,这样这个做法改进为 \(O(n^3)\)(kubic 那个题里 \(m\)\(10^9\))。

考虑可以快速求得这个 \(O(n)\) 次多项式 \(F(x)\),然后可以用伯努利数算出自然数幂和,这样就能在 \(O(n\log n)\) 的时间内算出 \(F(0\sim m-1)\) 的和。压力来到求 \(F(x)\)

这个 dp 形式长得非常格路计数,但是 \(j=\max\{j,0\}\) 很麻烦:它既让路径不能跨过 \(0\),又无法直接反射容斥上去,因为这个时候整个路径会出现某些时刻的步数纵坐标不变的情况(本来要么 \(+1\) 要么 \(-1\)),所以考虑把这个东西去掉。

这里有一个很厉害的双射! 从前往后扫描所有为 \(0\) 的位置 \(i\),然后把 \([0,i]\)\(y\) 坐标都 \(+1\)。这样终点没有变,起点变成了任意 \((0,a)\),且我们不会有 \((i,0)\rightarrow (i+1,0)\) 这样的走法,但是有一点是我们必须强制经过一次 \(y=0\) 的点

这个约束可以容斥掉:也就是强制所有坐标 \(\ge 0\)\(\ge 1\) 相减,两部分是类似的,以都 \(\ge 0\) 为例,此时由于起点不确定,我们枚举终点的纵坐标,然后枚举 \((i,j)\rightarrow (i+1,j+1)\) 这样的操作次数 \(k\),然后可以得到一个反射容斥的式子。

然后整理一下就会发现可以在 \(O(n^2)\) 的时间内求出多项式了,还能直接卷出来,就是 \(O(n\log n)\) 了。

记录

21. Kart Race

考虑这个问题也就是选择一个极大反链,直接做只能网络流 \(n^3\),但注意到这个题是个平面图,所以考虑可以用一些性质去优化。

如果是一棵树,我们其实就是选若干个互不包含的子树,可以求出 dfs 序以后做 dp。

平面图的性质也很优秀,所以我们来考虑如何简单判定平面图上的两点可达性:我们按斜率从上往下,从下往上做两边 dfs,然后令后序 dfs 序分别为 \(p,q\)。则点 \(x\) 能到达点 \(y\) 等价于 \(p_x \gt p_y\)\(q_x \gt q_y\)

然后就是一个很简单的 dp 了!按照 \(p\) 排序,然后在 \(q\) 这一维维护 bit。

我草字典序最小方案怎么构造?本来我们有一个 \(dp\) 数组,\(dp(i)\) 表示以第 \(i\) 个人结尾的最优答案,现在维护一个主席树,然后 \(dp(i)\) 从他的前驱再加入 \(i\) 得到。考虑比较,注意到如果两个节点编号不同,则它们内部的点一定是不同的,所以可以 \(O(\log n)\) 比较两个方案的字典序,这样在 bit 上的更新就是 \(O(\log^2)\) 了,但这里的主席树常数真的很小...... 时间复杂度 \(O(n\log^2 n)\)

记录

22. Minimize Inversions Number

这种题,怎么想到的.........

考虑 \(k=1\) 的情况,令 \(f_i\) 表示 \(i\) 前面比他小的元素个数 - \(i\) 前面比他大的元素个数,则我们会选择一个 \(f_i\) 最小的人提前。

对于 \(k\gt 1\) 的情况,考虑从前往后把子序列的数扔到前面,这样逆序对会增加 \(\sum f\),但是子序列的顺序反了。

所以设内部有 \(X\) 对顺序对,\(Y\) 对逆序对,则新的逆序对是 \(pre + \sum f - X + Y\)

到这里还是完全做不了,因为子序列内部的顺逆序对这种事情太抽象了......

有个想法是考虑子序列的选择本身有没有一些性质,发现如果有一个逆序对存在,而且你只选了前面那个不选后面那个,这个是不优的,不如选择后面那个。

所以子序列内部的逆序对数很好算,设 \(g_i\) 是原序列里 \(i\) 后面比他小的元素个数,则一个最优解的 \(Y\) 一定是 \(g\) 的和,且一个非最优解的 \(Y\) 一定不超过 \(g\) 的和。

验证一下,把 \(X\) 替换掉以后相当于 \(-\dbinom{k}{2} + \sum f + 2Y = -\dbinom{k}{2}+ \sum f + 2\sum g\),那么所有非最优解的实际答案都会比我们用等式右边这个东西算的值要小,而最优解的实际答案和算出来一定是一样的。

所以把 \(h_i = f_i + 2g_i\) 这个东西 sort 一下就好了,时间复杂度 \(O(n\log n)\)...

记录

23. 字符串

把我送走的题呃呃。但可能是 NOI D2T2 几年来最简单的了😭

彩蛋:场上因为 T1 做了很久,所以这个题看到后直接写了暴力 + A,然后 A 性质半天过不了 string3,一看四十多万行死掉的;调了很久最后输出了一下部分串,怎么有字符 h????然后才发现五组数据只有前两组是满足性质 A 的。还剩 20min 的时候会了 B 性质,冲完了但没时间测样例了,挂没了。

后来别人说,监考声明大样例性质了,但那个时候我去上厕所了......

考虑 B 性质保证了 \(S[i,i+l-1]\)\(RS[i+l,i+2l-1]\) 必定不完全相同,所以我们就是在 SA 上比较后缀 \(i\) 和前缀 \(i+2l-1\) 的大小关系,这是一个很简单的二维数点。

考虑一般情况就会有回文的情况去掉,发现若 \([i-l+1,i+l]\)\([i-r+1,i+r]\) 都是回文串,则后缀 \(i-l+1\) 与前缀 \(i+l\) 的大小关系,等价于后缀 \(i-r+1\) 与前缀 \(i+r\) 的大小关系。

因此我们只关注回文串的中心,枚举中心以后算出最长回文串,这里 manacher/二分 都可以,然后贡献又是一个简单的二维数点,带走了...

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

明年别来串了。

记录

24. 购物计划

我们 CCO 的题好厉害。

这个东西显然严格强于那个第 \(k\) 大子集(而且强很多),所以考虑直接上堆贪心的套路。

考虑关键点在于如何构建一个 \(O(1)\) 的非负权的,不重不漏的 trans。来考虑部分分。

对于 \(x=y=1\),这个就是是奶牛的某个题,考虑初始每个类都选最小的那个,然后拓展。拓展方式就是我们有一个指针 \(p\),初始指向 \(0\),每次把指针后移若干位,并且把新的 \(p\) 的所在类选第二小的,或者不移动 \(p\),直接选 \(p\) 所在类的后继。

但是后移若干位这个东西让 trans 集合大小变了,所以考虑每次一位一位动,那就要加上一个撤销:如果当前 \(p\) 指向第二小的,则当前类撤销成最小的,然后 \(p\) 右移一位并且此时所指向的类选第二小的。

那么为了保证非负性就按照 \(\min' - \min\) 排序就好了,这个还是很典的,直接拿下 60 昏啊。不过要注意判掉只有一种选择的类,特殊处理掉,后面也要做这个特判。

然后来考虑如果只有一个类,且不满足 \(x=y=1\) 怎么做?

还是把一个类的所有人排序,然后初始状态设置成最小的 \(x\) 个,然后考虑一个状态的极长前缀满足 \(p_i = i\),每次移动这个前缀的最后一个位置,向后移动若干位,为了保证 trans 集合大小的 \(O(1)\) 性质,所以我们每次移动一位。那么就要记录:当前移动的是第几个,移动到了哪个位置,最右能移动到哪里。注意由于 \(x\neq y\),所以我们可以把 \(y-x+1\) 个初始状态全部加进去。也可以当指针指向 \(cnt\) 的时候,加入一个转移到 \(cnt+1\) 的转移。

现在再回来考虑多个类的情况,实际上我们上面是对每个类支持了一个数据结构,使得我们计算这个类的前 \(k\) 优解可以做到均摊 \(k\log n\) 的复杂度。我们把这个过程套进 \(x=y=1\) 的时候的堆贪心即可,也就是堆贪心套堆贪心。

注意要特判一下 \(L=0\) 的类,他们会有些特殊。时间复杂度 \(O((n+m+k)\log)\)

记录

25. Insert 1, 2, 3, ...

600pt的 B 都这么难了?

考虑一个序列的判定:对于每个 \(a_i\gt 1\),我们将他和之前的一个 \(=a_i-1\) 的位置连边,要求:每个位置不能被使用多次,且不能出现相交不包含的情况(也就是交叉)。

我们考虑从前往后地为每个 \(a_i \gt 1\) 的选择决策,对于第 \(i\) 个人,如果他前面有若干个决策点可以选,我们一定会选择最靠右的(下标最大的)决策点,这样能让后续产生交叉的可能性最小。

当我们确定了 \(j\)\(i\) 匹配,实际上也就是要求 \(j\sim i-1\) 中的人不能再作为决策点;我们用并查集维护没有被 ban 过的点,把这些点 ban 掉就好了。然后我们可以 \(O(n\log n)\) 确定每个人的决策点 \(b_i\)

很显然一个区间合法等价于区间内所有 \(a\ge 1\) 的位置他们的 \(b\)\(\ge L\)。统计这样的区间数目是容易的,因此整个问题在 \(O(n\log n)\) 的时间内得到了解决。

记录

26. Negative Cost

625pt的 H 都这么难了?

考虑这个题比背包问题要强,而且有三个量:法力值 \(C\),伤害 \(H\),使用次数 \(x\)

注意到这题的 \(C\) 很小,所以我们可以把序列安排的比较均匀:也就是如果我们能执行想要的,消耗法力值的操作,就直接执行了,而不是屯着。

这样的话有两种情况:我们要么只用 \(C\lt 0\) 的操作,这样的话我们一定是一直用 \(H\) 最大的那个操作;要么任意时刻我们的法力值不会超过 \(2C = 600\),设其为 \(W\)

然后可以设 \(dp(i,j)\) 表示 \(i\) 个物品,剩下 \(j\) 的法力值,造成的最大伤害。但是我们的第一维度还是会很大。

还是根据鸽笼原理,我们发现会有很多前缀和是相同的。如果两个前缀和相同,都为 \(x\),则一定说明中间的 \(C\) 之和为 \(0\);这样一个区间我们总可以通过适当的安排,让段内的前缀和始终 \(\ge x\)(也就是如果只看这个区间,它的前缀和永远非负)。然后我们可以把这段区间原封不动搬到序列的结尾,也就是 \(ABC\rightarrow ACB\),其中 \(B\) 是我们调整后放到结尾的部分。然后会发现由于 \(A,B\) 结尾处前缀和相同,所以 \(C\) 这部分保持不动还是合法的。

用这个思路,可以证明任何一个序列可以拆成若干个部分完全独立地组合起来,每个部分的长度 \(\le W\)

因此对每个 \(i\le W\) 算出使用 \(i\) 次操作的情况下造成的最大伤害,然后变成了了一个两个量的多重背包问题,这个东西挺经典的。时间复杂度 \(O(W^3)\)

而且我们发现这对只使用 \(C\lt 0\) 的情况也成立,不用写特判了。

记录

27. Add Mod Operations

不会这个难度的 AGC C,我是不是没救了啊啊??

但是这个题感觉确实做起来很难啊呃呃呃。

考虑先判无解,一个显然的充分条件是存在两个 \(i\neq j\) 使得:\(a_i = a_j\) 但是 \(b_i\neq b_j\),猜测这个也是必要的。

也就是我们可以假设 \(a_i\) 互不相同,然后不妨认为 \(a_i\) 是升序的。

发现如果去掉 \(\bmod y\) 那这个操作就基本没用;但是加上 \(\bmod y\) 以后,一段后缀挪到前面这个东西又太复杂了。考虑每次令 \(y = \max a_n + x\),也就是让最大的那个人变成 \(0\),其它的 \(+x\)\(a_i\) 互不相同在这里得到了体现。)

发现这样迭代 \(n\) 轮后依然是按照 \(1,2,...,n\) 的顺序出现的而且任意两个数之间的空隙都可以任意决定(但一定不会是 \(0\)),可是我们一共只有 \(n\) 次操作的机会啊,除非 \(b_i\) 也严格升序否则这样做是出不了答案的。

考虑做 \(n-1\) 次,然后剩下一次留给一个另外的 \(+x,\bmod y\),那顺序就变成了 \(2,3,4,...,n,1\)。且新的 \(a'_2=0\),然后每个空我们都可以自由决定,但是 \(n\)\(1\) 的空隙至少是 \(a_1\)

考虑设一个足够大的 \(K\),然后只要令 \(n-1\) 次操作后,\(a_i\) 变成了 \(b_i - b_2 + rk(i)\times K\)\(rk(1)=n,rk(i)=i-1\),然后我们最后做一次 \(+b_2\)\(\bmod K\) 即可。

这个也是能做的,因为 \(K\) 足够大,所以相邻两个数的目标值差分一下也一定足够大(因此也大过 \(a_1\)),这样就在 \(O(n)\) 次内完成了构造。

注意特判 \(n=1\),因为这个时候 \(n-1\) 次操作完,序列的第一项不一定为 \(0\)

代码写起来很短,但真的很神。

记录

posted on 2023-07-04 10:06  Cry_For_theMoon  阅读(257)  评论(5编辑  收藏  举报