NOIP2023做题记录

目录

Luogu

P5746 [NOI2002] 机器人M号

传送门

有DP解法和数学解法。

DP解法显然,设 \(f[i][j](j \in \left \{ 0,1 \right \})\) 表示前 \(i\) 个素数中,选了奇/偶数个因子的独立数的和,则易得方程 \(f[i][j]=f[i-1][j \otimes 1]\times\varphi(p_i)+f[i-1][j]\)

数学解法还没看懂。

P5150 生日礼物

传送门

考虑唯一分解定理 \(N=\prod p_i^{k_i}\) 。而 \(\forall N\in\mathbb{N^{+}}\) ,都有一组唯一的分解序列 \(S=\left \{ k_1,k_2,\dots,k_n \right \}\)

故,\(\forall x,y\in\mathbb{N^{+}}\) ,有 \(x=\prod p_i^{a_i},y=\prod p_i^{b_i}\) ,则有 \(\gcd(x,y)=\prod p_i ^{\min(a_i,b_i)},lcm(x,y)=\prod p_i ^{\max(a_i,b_i)}\)

而本题则是要求存在多少组 \((x,y)\) 满足 \(N=lcm(x,y)\)

于是我们考虑先对 \(N\) 分解质因数,得到 \(S=\left \{ k_1,k_2,\dots,k_n \right \}\) 。注意到这里 \(k_i=\max(a_i,b_i)\) ,则必然有其中之一是 \(k_i\) ,而另一个数的取值为 \([0,k_i]\) 。注意要减去 \(a_i=b_i=k_i\) 的情况,故每一位 \(p_i\) 对答案的贡献为 \(2\times k_i+1\) 。于是答案即为 \(\prod (2\times k_i+1)\)

而这题是个屑卡常题,但 \(O(\sqrt N)\) 也可以通过本题了。注意最后除去所有小于 \(\sqrt N\) 的因子的 \(N'\) ,如果 \(N'>1\) ,即是说还剩下一个质因子,也要对答案有 \(\times 3\) 的贡献。

P1989 无向图三元环计数

传送门

感觉是一道很 tricky 的题。

注意到如果不做任何处理直接枚举显然会 T ,于是考虑优化。

注意到,如果我们人为地给图中的每条无向边定向,则会得到一个 DAG 。具体方法如下:令度数大的点连向度数小的点,如果两个点的度数相同,则人为定向,比如可以按编号从大指向小。

然后,我们考虑,如果有 \(a\to b\)\(a\to c\) 两条边,那么只要满足 \(b\to c\) 则一定是一个三元环(显然,此处 \(b,c\) 等价),于是可以计数。此时可以证明时间复杂度为 \(O(m\sqrt m)\)

P5686 [CSP-S2019 江西] 和积和

传送门

拆式子的入门题。

首先设 \(SA_{n}=\sum^{n}_{i=1}a_i,SB_{n}=\sum^{n}_{i=1}b_i\) ,于是 \(S(l,r)=(SA_{r}-SA_{l-1})\times (SB_{r}-SB_{l-1})\)

\(\begin{aligned}\sum_{l=1}^n \sum_{r=l}^n S(l,r)&= \sum_{l=1}^n \sum_{r=l}^n (SA_{r}-SA_{l-1})\times (SB_{r}-SB_{l-1})\\&=\sum_{l=1}^n \sum_{r=l}^n(SA_r\times SB_r+SA_{l-1}\times SB_{l-1}-SA_r\times SB_{l-1}-SA_{l-1}\times SB_r)\end{aligned}\)

这样做是 \(O(n^2)\) 的,需要进一步拆式子来降低复杂度,由数据范围可知应当做到 \(O(n)\)\(O(n\log n)\)

\(C_n=SA_n\times SB_n,T_n=\sum^{n}_{i=1}C_i,TA_{n}=\sum^n_{i=1}SA_i,TB_{n}=\sum^n_{i=1}SB_i\)

\(\begin{aligned}上式&= \sum_{l=1}^n \sum_{r=l}^n(SA_r\times SB_r+SA_{l-1}\times SB_{l-1})-\sum^{n}_{l=1}\sum^n_{r=l}(SA_r\times SB_{l-1})-\sum^{n}_{l=1}\sum^n_{r=l}(SA_{l-1}\times SB_r)\\&=\sum^{n}_{l=1}(T_{n}-T_{l-1}+(n-l+1)\times C_{l-1})-\sum^n_{l=1}SB_{l-1}\times(TA_{n}-TA_{l-1})-\sum^n_{l=1}SA_{l-1}\times(TB_{n}-TB_{l-1})\\&=\sum^{n}_{l=1}(T_{n}-T_{l-1}+(n-l+1)\times C_{l-1}-SB_{l-1}\times(TA_{n}-TA_{l-1})-SA_{l-1}\times(TB_{n}-TB_{l-1}))\end{aligned}\)

至此可以 \(O(n)\)得出答案。

P9533 [YsOI2023] 区间翻转区间异或和

传送门

诈骗题。首先给出结论,翻转操作是无效的。

现证明:不妨设有两个相交的灵异区间 \([x,y],[l,r]\) 满足 \(x<l<y<r\) 。设 \(A=\bigoplus ^{l}_{i=x}a_i,B=\bigoplus ^{y}_{i=l}a_i,C=\bigoplus ^{r}_{i=y}a_i\) 。由题设, \(A\oplus B=B\oplus C=0\) ,于是有 \(A\oplus C=0\) ,灵异区间并没有增加。

而存在一个灵异区间要满足 \(s_{l-1}\oplus s_r=0(l<r)\) ,换言之有 \(s_{l-1}=s_r\) ,其中 \(s_i=\bigoplus^{i}_{j=1}a_j\) 。我们不妨将 \(s\) 数组排序一下,记 \(s_l=\cdots=s_{l+k-1}=C\) ,于是该段对答案的贡献为 \(k\times(k-1)/2\)

P9166 [省选联考 2023] 火车站

传送门

形式化题面:给定 \(m\) 条线段,覆盖点 \(x\) 的线段我们定义为该线段合法,与合法的线段有任何重合的线段也合法(包括端点重合),求合法线段的端点。
我们考虑题中的线段实际上只有 \(3\) 种情况:

  1. 跨过 \(x\) 点。
  2. \(x\) 点左侧。
  3. \(x\) 点右侧。

我们首先可以将情况 \(1\) 中点作为合法答案,并记一个最小的 \(L\) 和最大的 \(R\) 表示当前可扩展到的边界。

对于情况 \(2\) ,注意到,当且仅当该线段的右端点在 \([L,R]\) 内,于是可以将其左端点加入答案(注意题目要求不能转向)并更新 \(L\) 。情况 \(3\) 同理。同时注意遍历顺序,需要排序保证其端点单调性。

P5749 [IOI2019] 排列鞋子

传送门

小清新的贪心。不难想到一个贪心策略:从左往右遍历每个鞋子,找到相同权值且编号最小的鞋子即完成配对。可以证明该贪心策略是最优的。

我们人为地给匹配后的序列编号。从左向右扫,第 \(i\) 双鞋子的左鞋定为 \(2\times i-1\) ,右鞋定为 \(2\times i\) 。此时我们可以观察编号序列,以样例 \(1\) 为例,编号序列为 2 4 3 1 ,不难发现交换次数即逆序对数。

求逆序对的复杂度是 \(O(n\log n)\) 的,于是本题瓶颈在于匹配。暴力匹配是 \(O(n^2)\) 的,显然不可以接受,考虑优化。

注意到无论是左鞋还是右鞋,匹配时都是从左到右找到第一个符合条件的鞋子。于是,我们可以用 map<int,priority_queue<int,vector<int>,greater<int>>> q 来维护一个小根堆, q[x].top() 表示值为 \(x\) 的小根堆堆顶。

P1360 [USACO07MAR] Gold Balanced Lineup G

传送门

记能力提升数字为 \(x\) ,记 \(a\) 数组为对应每一位上的能力值大小。由题意,为达成均衡时期的条件,需要有 \(\forall i,k\in[1,m],a_i=a_k+C\)

我们为得到本质不同的 \(a\) 数组,可以额外记录一个 \(b\) 数组,满足 \(b_1=0,b_i=a_i-a_1(i\ge2)\) ,然后用 Hash 来存储对于每个 \(x\)\(b\) 数组是什么。其中,注意在最初的时候 \(\forall i\in[1,m],b_i=0\) 。于是如果在后续某个时刻 \(k\in[1,m]\) 也满足该条件,答案可以直接被更新为 \(k\)

注意:本题模数太小时容易引发 Hash 碰撞。根据生日攻击原理,模数应远大于 \(n^2\) 才比较合适。

P2168 [NOI2015] 荷马史诗

传送门

形式化题面:现在有一棵有 \(n\) 个叶子节点的 \(k\) 叉树,每个叶子节点有一个权值 \(w_i\) ,该节点的深度为 \(h_i\) ,现在要最小化 \(\sum w_i h_i\)

本题是 \(k\) 叉哈夫曼树板子。显然地,将权值越小的节点放在越深的位置越优且满足条件 \(k\) 叉树的最后一层叶子是满的。考虑每次将最深的 \(k\) 个叶子合并为 \(1\) 个新的叶子结点,那么我们会减少 \(k-1\) 个节点。最后 \(n\) 个叶子节点会变成 \(1\) 个叶子节点,也就是减少 \(n-1\) 个叶节点。于是我们要手动补 \(0\) 占位,从而满足 (n-1)%(k-1)==0 。如此,我们的操作次数恰是整数次。

本题的合并操作用小根堆模拟即可。

P8590 『JROI-8』这是新历的朝阳,也是旧历的残阳

传送门

呃呃,傻逼题调了我半天。

注意到直接做是 \(O(nk)\) 的,考虑优化。

根据题意,显然是有部分数放在第 \(1\) 段,剩下的数放在第 \(m\) 段。直觉上是负数在第 \(1\) 段,正数在第 \(m\) 段。但这是错的,因为 \(\exist x<0,(x+1)^2<(x+m)^2\) 。记 \(p\) 为最后一个 \(a_p<0\) 的位置,于是我们可以枚举分割点 \(p\) ,然后不断左移 \(p\)

\(\begin{aligned}q_m&=\sum^{n}_{i=1}b_i^2=\sum^{p}_{i=1}b^2_i+\sum^{n}_{i=p+1}b^2_i=\sum^{p}_{i=1}(a_i+1)^2+\sum^{n}_{i=p+1}(a_i+m)^2\\&=\sum^{n}_{i=1}a_i^2+2 \sum^{p}_{i=1}a_i+2 m\sum^{n}_{i=p+1}a_i+p+(n-p)m^2\end{aligned}\)

简单拆一下式子即可,同时注意取模中可能出现的种种问题。

P8572 [JRKSJ R6] Eltaw

传送门

注意到 \(1\le n\times k\le 5\times10^5\) ,考虑分类讨论 \(n,k\) 的大小关系。其中, \(\sqrt{5\times10^5}\approx 707\)

\(1\le k\lt n \lt1\times10^3\) 时,考虑在线。对于每个询问遍历 \(k\) 行处理出答案,时间复杂度为 \(O(n+qk)\)

\(1\le n\lt k\lt1\times10^3\) 时,考虑离线。我们预处理出每行每个区间的答案,时间复杂度为 \(O(n^2k+q)\)

P6878 [JOI 2020 Final] JJOOII 2

传送门
一道小清新题。显然考虑要分割成 \(3\) 段,使得每段中各有 \(k\) 个所需的字母。

\(3\) 个前缀和分别记录每个字母在前缀出现的次数。我们可以枚举 \(l\) 作为字母 \(J\) 的区间左端点。由题意,需要有 \(SJ_r-SJ_{l-1}=k\) ,也即 \(SJ_r=SJ_{l-1}+k\) 。我们可以用 lower_bound 得到等于的位置,此时得到的是字母 \(J\) 的区间右端点 \(r\) 。然后同理,以 \(r+1\) 为字母 \(O\) 的区间左端点,重复上述操作即可。

P9583 涂色

传送门

小清新题。

记数组 \(r,c\) 分别表示行、列染色的情况。正难则反,我们考虑计算哪些点没有被染色,则需满足 \(r_i +c_j\equiv 0\pmod k\Leftrightarrow r_i \equiv -c_j\pmod k\)

于是我们开个桶记录一下每个 \(r_i\pmod k\) 的值,然后遍历 \(c\) 数组累加 b[(k-c[i])%k] 即可,总格点数减去无色点即答案。

*P9584 城市

传送门

换根dp板子。若未指明,则默认 \(w=w_{u\to v}\)

考虑可以先计算出 \(\begin{aligned}\sum^{n}_{i=1}\sum^{n}_{j=1}cost(i,j)\end{aligned}\) ,然后再考虑新增加一个点的贡献。记 \(dis_u\) 为点 \(u\) 到所有点的距离和。显然,贡献为 \(2\times(dis_k+n\times w)\) 。然后考虑怎么计算 \(\begin{aligned}\sum^{n}_{i=1}\sum^{n}_{j=1}cost(i,j)=\sum^{n}_{i=1}dis_i\end{aligned}\)

做题的时候因为思维过于定式了,所以考虑了子树内和子树外的情况。记 \(f_u\) 表示点 \(u\) 到子树内所有点的距离和, \(g_u\) 表示点 \(u\) 到子树外所有点的距离和,则 \(dis_u=f_u+g_u\)

显然 \(\begin{aligned}f_u=f_v+\sum_{v\in son_u}(f_v+sz_v\times w),g_v=g_{fa}+\sum_{v\in son_{fa}\setminus \left \{ v \right \} }(f_v+sz_v\times w_{fa\to v})+(n-sz_u)\times w_{fa\to u}\end{aligned}\)

此时 \(f_u\) 可以在第一次 dfs 时 \(O(n)\) 计算得到,但 \(g_u\) 直接计算的话将是 \(O(n^2)\) 的,考虑优化。

我们注意到,对于每个 \(v\) 而言, \(\begin{aligned}\sum_{v\in son_{fa}\setminus \left \{ v \right \} }(f_v+sz_v\times w_{fa\to v})\end{aligned}\) 是可以在第一次 dfs 时预处理出来的。记在点 \(u\) 前的贡献为 \(pre_u\) ,在点 \(u\) 后的贡献为 \(suf_u\) ,于是 \(g_v=g_{u}+pre_v+suf_v+(n-sz_v)\times w\)

但实际上,我们可以直接考虑 \(dis_v=dis_u-sz_v\times w+(n-sz_v)\times w=dis_u+(n-2\times sz_v)\times w\) ,该式可以通过 \(dis_u=f_u+g_u\) 推得。而考虑实际意义的话,也就是和板子一样,每走过一条边 \((u,v,w)\) 时,便会有 \(sz_v\) 个节点减少 \(w\) 的贡献,会有 \((n-sz_v)\) 个点增加 \(w\) 的贡献。

此外,本题还有所谓的拆式子算贡献的标算做法。待补。但感觉不如直接换根dp。

P9437 『XYGOI round1』一棵树

传送门

换根dp,但细节有点多。看了题解发现都比我的做法简单,甚至只要一次 dfs 。

\(f_u\) 为以 \(u\) 为根的子树内(含 \(u\) )对答案的贡献之和, \(g_u\) 为以 \(u\) 为根的子树外(含 \(u\) )对答案的贡献之和,这里这样设状态方便后续转移。故答案即为 \(\sum (f_i+g_i-a_i)\) ,因为显然 \(u\to u\) 的路径被走了两次。设 \(a_i\) 的位数为 \(x\) 位(显然,我们认为 \(0\)\(1\) 位),设 \(cnt_i=10^x\) ,用以拼接数字。

\(f_u\) 的转移是显而易见的,我们考虑怎么转移 \(g_u\) ,注意到本题中兄弟节点会对当前节点产生影响,故我们在第一次 dfs 时先预处理出前缀贡献和与后缀贡献和,记为 \(pre_u\)\(suf_u\)

\(\begin{aligned}f_u&=a_u\times(sz_u-1)+\sum_{v\in son_u}f_v\times cnt_u\\g_u&=a_v\times(n-sz_v+1)+(g_u+pre_v+suf_v+(sz_u-sz_v-1)\times a_u)\times cnt_v\end{aligned}\)

其中,第一次 dfs 为自下而上计算,第二次 dfs 为自上而下计算,复杂度 \(O(n)\) 即可解决本题。

P3178 [HAOI2015] 树上操作

传送门

树剖板子,但可以用 dfs 序拍扁到序列上做,这里只是记录一下这个思路。

我们可以用线段树的叶子结点存储树上某点到根节点的点权和。考虑设 \(t_1,t_2\) 两个数组。其中,答案为 \(dep_u\times t_1[p]+t_2[p]\)

考虑操作 \(1\) ,对 \(u\) 子树内每个点 \(v\) 的贡献均为 \(k\) ;考虑操作 \(2\) ,对 \(u\) 子树内每个点 \(v\) 的贡献为 \((dep_v-dep_u+1)\times k\) 。而此时 \(v\) 是一连串的点,这也是为什么我们要设 \(t_2\) 来进行加减法运算。

另外最初建树的时候要这样写。for(int i=1;i<=n;++i){modify(1,1,n,dfn[i],dfn[i]+sz[i]-1,0,a[i]);}

P2486 [SDOI2011] 染色

传送门

想通了其实感觉也没那么难呃呃,调这种题能发现挺多问题的。基本是重链剖分部分,这里主要讲一下怎么维护颜色段。

考虑设 lc,rc,cnt,tag 分别维护区间左端点颜色、区间右端点颜色、区间颜色段个数和区间将被修改为什么颜色。注意 pushup 计算 cnt 时,若 ls(p).rc=rs(p).lc ,则最后还要减一,因为显然两个相同颜色段合并在一起时多计算了一个。在 query 时同理,若 \(L\le mid\lt R\) 且满足上述条件,同样要减一。

但是线段树部分只能在重链部分是有效的,树剖就是把两点之间剖成了若干条链,我们还是要认为去合并各条重链。于是在 query_path 时,还需判断每次更新前的轻儿子的左端点和更新后的左端点颜色是否相同,如果相同颜色段数量减一。这里可以用类似单点查询的写法查询单点的颜色,此时两个要查询的点分别是 top[x]fa[top[x]]

P3359 改造异或树

传送门

nfls某天模拟赛的 E 题。既然删边不好处理,那我们就考虑倒着加边。

其次注意到一个性质: \(u,v\) 是树上的两点,记 \(S_u\) 表示从 \(root\to u\) 的简单路径异或和,则 \(u\to v\) 的简单路径异或和即 \(S_u \oplus S_v\) 。于是我们可以钦定 \(1\) 为根,预处理出两点间的简单路径异或和。然后维护一个 map<int,int> Map[N] 做启发式合并,其中, map[x][val]=cnt 表示在 \(x\) 所在的连通块内有 \(cnt\) 个权值为 \(val\) 的路径。注意到 \(a\oplus a=0\) ,于是每次加边对答案的贡献即为 Map[x][val]*Map[y][val]

P3960 [NOIP2017 提高组] 列队

传送门

本题是 NOIP2017 D2T3 ,还是挺有意思的。部分分的设置也很好,很有教育意义,整体思考下去很顺畅,正解也就随着部分分呼之欲出了。


30pts:

开一个 \(n\times m\) 的数组暴力模拟。时间复杂度 \(O(nmq)\)


50pts:

因为 \(q\le 500\) ,所以只会有 \(500\) 行发生改动。注意到每次向左对齐后,空位一定会出现在最后一列。因此向前对齐时只有最后一列会发生移动,所以总共只要维护 \(500\) 行和最后一列。时间复杂度 \(O(q(n+m))\)


70pts:

\(n=1\) 说明只有一行,每次离队相当于把一个人从队伍里抽出来放到队尾。这是一个经典的线段树/树状数组问题:维护一个长度为 \(L\) 的区间,每个位置上有可能是空的,有可能有一个下标。每次查询第 \(k\) 个非空位置上的下标。

对于每个位置如果非空就记为 \(1\) ,否则记为 \(0\) 。相当于查询前缀和为 \(k\) 的下标。线段树/树状数组上二分即可。

如何处理离队再归队?

初始时令前 \(m\) 个位置非空,后 \(q\) 个位置为空。第 \(i\) 个事件中,第 \(k\) 个人离队时,把第 \(k\) 个位置标记为 \(0\)(表示离队),第 \(m+i\) 个位置标记为 \(1\)(表示归队)。时间复杂度为 \(O((m+q)\log(m+q))\)


80pts:

事件中 \(x=1\) ,因此每一次归队后,第 \(1\) 行的第 \(m\) 列变成了原来在第 \(2\) 行第 \(m\) 列的人,离队的人回到了第 \(n\) 行第 \(m\) 列。可以发现在变化的只有第 \(1\) 行和第 \(m\) 列这些数。

对于第 \(1\) 行还是用线段树维护,对于第 \(m\) 列维护一个队列即可。每次只需要取出队头放在第 \(1\) 行的线段树中,再把第一行离队的人放到队尾。时间复杂度仍为 \(O((m+q)\log(m+q))\)


100pts:

类似 \(n=1\) 的情况,用线段树/树状数组来查询这一行第 \(k\) 个非空位置即可。如果我们还是用 \(0/1\) 表示空/非空,那么初始的时候每一行的前 \(m\) 个位置都是 \(1\)\(n\) 个线段树开不下来。所以用一个动态开点的线段树即可。维护 \(n\) 个行线段树(不包含最后一列)和最后一列的线段树。对于插入到队尾的编号,我们用 vector 来维护。

\((x,y)(y<m)\) 离队时:首先,查询第 \(x\) 个行线段树中的第 \(y\) 个非空坐标的编号 \(id_1\),并记为空。然后,查询最后一列线段树中的第 \(x\) 个非空下标的坐标 \(id_2\) ,并记为空。最后,将 \(id_1\) 放进第 \(x\) 行的 vector 队尾,将 \(id_2\) 放进最后一列的 vector 的队尾。

\((x,y)(y=m)\) 离队时:查询最后一列线段树中的第 \(x\) 个非空的下标 \(id\) ,并记为空。然后将 \(id_1\) 放进最后一列的 vector 的队尾。

注意如果编号在原先位置则可以通过计算得到,反之则需要从 vector 的对应位置查到。时空复杂度均为 \(O(q\log(m+q))\)

题外话:不动态开点的话上平衡树也行。

P9619 生成树

传送门

在梦里的时候想明白了,挺小清新的。

显然首先我们需要考虑每条边会出现多少次。由 Cayley 定理知完全图 \(G_n\)\(n^{n-2}\) 棵生成树,每棵树有 \((n-1)\) 条边,而完全图共有 \(n(n+1)/2\) 条边。注意到完全图内每条边地位相同,故每条边出现了 \(2n^{n-3}\) 次。

然后我们考虑怎么求完全图的边权和 \(\begin{aligned}\sum_{1\le i<j\le n}a_i\oplus a_j\end{aligned}\) 。显然直接求是 \(O(n^2)\) 的,不可接受。考虑异或的性质,第 \(k\) 位上会对答案产生贡献,当且仅当 \(a_i\)\(a_j\) 在第 \(k\) 位上的值不同,即一个为 \((0)_2\) ,另一个为 \((1)_2\) 。于是我们可以枚举每一位,显然每两个 \((0)_2\)\((1)_2\) 就可以产生贡献。设 \(f_i\) 表示从右往左数第 \(i\) 位为 \(1\) 的点权的数量。故 \(\begin{aligned}\sum_{1\le i<j\le n}a_i\oplus a_j=\sum 2^{i-1}\times f_{i}\times (n-f_{i})\end{aligned}\) 。而每条边都出现了 \(2n^{n-3}\) 次,故再乘上次数即可。

P9618 地铁

传送门

注意到 \(c\le 20\) ,这是本题的突破点,考虑枚举换乘次数 \(y\) 。对于一个询问,考虑小于 \(c\) 的每个 \(y\) ,显然此时令 \(x\) 最小即可。

假设此时求出了换乘不超过 \(k\) 次时的最短距离,由于只有一次换乘,所以不会存在 \(A\) 更新 \(B\)\(B\) 用被更新的值再更新 \(C\) 的情况,所以直接枚举每条地铁线路维护乘上车的最短距离进行朴素递推即可。注意在递推时是没有换乘的,换乘存在与两个状态见的转移。

P3398 仓鼠找 sugar

传送门

要求判断树上两条路径有没有交,若有交,则需满足一条路径的两端点的 lca 在另一条路径上。形式化地说,设路径为 \(a\sim b\)\(c\sim d\) ,令 \(t=lca(a,b)\) ,若有 \(dis(c,t)+dis(d,t)=dis(c,d)\) ,则有交。

暴力做法是树剖,对一条路径区间赋值为 \(1\) ,然后查询另一条路径的区间或和。

拓展:给定一棵树,其中一些链被标记有权值。你需要选择一些不重叠的链,并最大化选出的链的权值和。其中,节点个数 \(\le2\times10^5\) ,带权链个数 \(\le15\)

传送门

显然我们已经知道了怎么 \(O(n\log n)-O(1)\) 判断两条链是否有交,那么这里只要 \(O(2^{15})\) 枚举即可,甚至都不用写状压,很简单吧。

P1016 [NOIP1999 提高组] 旅行家的预算

传送门

注意到这样的一个贪心策略:考虑每次都消耗最便宜的油且都给油箱加满,如果到达下一个加油站时,油价更便宜,则考虑将之前买的贵的油都替换成便宜的油。于是可以用 deque 手写单调队列实现。

P3619 魔法

传送门

首先对于满足 \(b_i\ge0\) 的任务,显然按 \(t\) 从小到大排序依次完成即可。

其次对于满足 \(b_i<0\) 的任务,考虑怎么样的顺序需要我们交换任务 \(i\) 和任务 \(i+1\) 。显然是先完成任务 \(i+1\) 后仍能完成任务 \(i\) ,但先完成任务 \(i\) 不能先完成任务 \(i+1\) 的情况。我们不妨列式,即有 \(T+b_{i+1}>t_i\)\(T+b_i<t_{i+1}\) 。移项得, \(t_i-b_{i+1}<T<t_{i+1}-b_i\Rightarrow t_i+b_i<t_{i+1}+b_{i+1}\) 。于是,我们按照 \(t_i+b_i\) 从大到小排序依次完成即可。

P4768 [NOI2018] 归程

传送门

首先注意到在 \(v\)\(1\) 的归程上,肯定是先开车一段,然后步行回家的,故必定有一个分界点 \(k\) 。于是我们可以枚举 \(k\) ,满足 \(k\)\(v\) 的路径上所有的边的海拔都大于 \(p\) 的情况下,要求 \(1\)\(k\) 的最短路最短。

于是我们考虑哪些点满足从 \(v\) 出发,路径上所有边的海拔都大于 \(p\) 。为此我们可以用将原图的海拔按从大到小排序建出 \(\operatorname{Kruskal}\) 重构树,这保证了重构树一定是一个小根堆,即以 \(u\) 为根的子树内的节点点权都不小于点 \(u\) 点权,此时我们认为点权是海拔高度。于是对于每次询问求出包含 \(v\) 的子树中根节点深度最小并且点权大于 \(p\) 的节点 \(u\) ,那么以 \(u\) 为根的子树内的所有节点都可以由 \(v\) 开车到达。可以用树上倍增找到点 \(u\)

于是我们在以 \(u\) 为根的子树内枚举断点 \(k\) ,那么我们只要查询到子树内的最小点权即可,此时我们认为该子树内的点权为点 \(k\) 到 点 \(1\) 的最短路,可以用一次 \(\operatorname{Dijkstra}\) 求出点 \(1\) 到其他所有点的最短路。而查询子树内的最小点权完全可以在建 \(\operatorname{Kruskal}\) 重构树的同时更新,因为重构树的形态是唯一确定的。

P5663 [CSP-J2019] 加工零件

传送门

首先考虑转变一下题意,变为:询问从点 \(a\) 到点 \(1\) 是否存在长度为 \(L\) 的路径。然后注意到一个显然的性质,如果从点 \(a\) 到点 \(1\) 存在长度为 \(L\) 的路径,则同样存在长度为 \(L+2k(k\in \mathbb{N^*})\) 的路径,因为显然可以在某两点间来回走。于是问题又变为了:若 \(L\) 为奇数,则从点 \(a\) 到点 \(1\) 的奇数最短路径长度是否小于等于 \(L\) ;若 \(L\) 为偶数同理。于是就跑一个分层图最短路即可。

U330377 前缀和差分维护等差数列

传送门

一个长度为 \(n\) 的数组,问 \(m\) 次操作后的该数组。
每次操作为对区间 \([l, r]\) 添加一个首项为 \(s\) ,公差为 \(x\) 的等差数列。
即,\(a[l] += s,\ a[l + 1] += s + x,\ a[l+2] += s + 2x,\dots, a[r] += s + (r - l)\times x\)\(1\le n,m\le10^5\)

首先注意到,显然可以将操作拆分为两部分,前者是对 \([l,r]\) 增加 \(s\) ,后者是对 \([l+1,r]\) 增加等差数列个 \(x\) 。由于后者的存在,所以我们需要用二阶差分来刻画。

如下表所示,先考虑前者。显然,我们可以根据当前数列给出差分后的数列。

\(l-1\) \(l\) \(l+1\) \(l+2\) \(\dots\) \(r\) \(r+1\) \(r+2\) \(r+3\)
\(+0\) \(+s\) \(+s\) \(+s\) \(\dots\) \(+s\) \(+0\) \(+0\) \(+0\)
\(+0\) \(+s\) \(+0\) \(+0\) \(\dots\) \(+0\) \(-s\) \(+0\) \(+0\)
\(+0\) \(+s\) \(-s\) \(+0\) \(\dots\) \(+0\) \(-s\) \(+s\) \(+0\)

后者同理。

\(l-1\) \(l\) \(l+1\) \(l+2\) \(\dots\) \(r\) \(r+1\) \(r+2\) \(r+3\)
\(+0\) \(+0\) \(+x\) \(+x\) \(\dots\) \(+(r-l)x\) \(+0\) \(+0\) \(+0\)
\(+0\) \(+0\) \(+x\) \(+x\) \(\dots\) \(+x\) \(-(r-l)x\) \(+0\) \(+0\)
\(+0\) \(+0\) \(+x\) \(+0\) \(\dots\) \(+0\) \(-(r-l+1)x\) \(+(r-l)x\) \(+0\)

U362250 仍未知道的异或为X的区间的个数

传送门

题意:给定数组 \(a\) ,要求有多少个区间 \([l,r]\) 满足 \(\bigoplus ^{l}_{i=r}a_i=X\)\(1\le n\le10^5\)

\(s_i=\bigoplus^{i}_{j=1}a_j\) ,则是要求满足 \(s_{l-1}\oplus s_r=X\)\((l,r)\) 对数。等号两边同时异或 \(s_r\) 可变为 \(s_{l-1}=X\oplus s_r\) 。于是考虑枚举 \(r: 1\sim n\) ,统计前缀的合法对数即可。

P2704 [NOI2001] 炮兵阵地

传送门

考虑用状压来解,记一行内状态为 \(S\) ,其某一个二进制位上为 \(1\) 当且仅当该位放炮兵。设 \(f_{i,j,k}\) 表示前 \(i\) 行,第 \(i\) 行状态为 \(j\) ,第 \(i-1\) 行状态为 \(k\) 的最大值。

首先我们可以预处理出 \(S\) 存储一行内满足有炮兵的位置相隔 \(2\) 以上。记 \(i:0\sim 2^m-1\) 表示当前状态,显然需要满足 !(i&(i<<1))&&!(i&(i<<2)) 。虽然 \(2^m\) 很大,但可以发现 \(|S|<100\) 。故而我们可以离散化一下,改设 \(f_{i,j,k}\) 表示前 \(i\) 行,第 \(i\) 行状态为 \(S_j\) ,第 \(i-1\) 行状态为 \(S_k\) 的最大值。我们不妨设 \(cnt_i\) 表示 \(S_i\)\(1\) 的个数,在上述循环中一起预处理出来。

然后我们枚举 \(i:1\sim n\) 表示当前枚举到第 \(i\) 行,枚举 \(j,k,l:1\sim |S|\) 表示第 \(i,i-1,i-2\) 行的合法状态为 \(S_j,S_k,S_l\)。为满足不误伤的条件,则要有 !(s[j]&s[k])&&!(s[j]&s[l]) 。此外,还要判断在第 \(i\) 行时 \(S_j\) 的状态是否满足每个放炮兵的地方都是平原。可以记 \(valid_i\) 表示第 \(i\) 行的地形,其某一个二进制位上为 \(1\) 当且仅当该位为山地。故只需保证 !(s[j]&valid[i]) 即可。转移方程为 \(f_{i,j,k}=\max(f_{i-1,k,l}+cnt_j)\)

最终答案为 \(\begin{aligned}\max_{1\le i,j\le |S|}(f_{n,i,j})\end{aligned}\) 。总时间复杂度为 \(O(n|S|^3)\)

P3959 [NOIP2017 提高组] 宝藏

传送门

注意到打通房间的代价和顺序无关,故我们可以按层由浅到深状压。设 \(f_{i,j}\) 表示已经打通的最大深度为 \(i\) ,房间的打通状态为 \(j\) 时的最小代价, \(j\) 的某一位为 \(1\) 当且仅当该位的房间已经被打通。由题意有 \(\begin{aligned}f_{i,j}=\min_{\operatorname{valid(j,k)}}(f_{i-1,k}+(i-1)\times \operatorname{cost(k,j)})\end{aligned}\) 。其中, \(\operatorname{valid(j,k)}\) 表示由状态 \(k\) 转移到状态 \(j\) 是否合法,在此先按下不表, \(\operatorname{cost(j,k)}\) 表示由状态 \(k\) 转移到状态 \(j\) 增加的道路长度的最小值。

由题意可以任选一点为根,故初始化 \(f_{1,2^i}=0(i:0\sim n-1)\) 。最终答案显然为 \(\begin{aligned}\min_{1\le i\le n}(f_{i,2^n-1})\end{aligned}\)

考虑到我们是按层遍历, \(\operatorname{valid(i,j)}\) 就需要状态 \(j\) 拓展到状态 \(i\) 且只拓展了一层。 \(\operatorname{valid(i,j)}\) 为真需要满足满足两个条件:

  • 状态 \(j\) 是状态 \(i\) 的子集,即 \(i\&j=j\)
  • 状态 \(i\)\(\operatorname{expand(j)}\) 的子集,即 \(i\&\operatorname{expand(j)}=i\) 。其中, \(\operatorname{expand(j)}\) 表示由状态 \(j\) 中的所有房间出发,向下开凿一层以内的所有道路之后,可以到达的状态。

首先枚举 \(i:0\sim 2^n-1\) 表示 \(2^n\) 种状态,令 \(\operatorname{expand(i)}=i\) ,由此开始拓展来预处理出 \(\operatorname{expand(i)}\) 。枚举 \(j:1\sim n\) 表示 \(n\) 个房间来枚举已经打通的房间,记 \(\operatorname{road(i,j)}\) 表示状态 \(i\) 时到达房间 \(j\) 需要新打通的最短道路。若 \(i\) 的第 \(j-1\) 位为 \(1\) ,则表示房间 \(j\) 已经被打通了,令 \(\operatorname{road(i,j)}=0\) 。然后再枚举 \(k:1\sim n\) 表示 \(n\) 个房间来枚举还未打通的房间,更新 \(\operatorname{expand(i)}\)\(\operatorname{road(i,k)}\)

然后枚举 \(i:0\sim 2^n-1\) 表示 \(2^n\) 种状态,枚举 \(i\) 的子集 \(j\) ,同时还要让 \(j\) 满足 \(i\&\operatorname{expand(j)}=i\) 。特别地,可以如下附代码这么写,这样比直接枚举 \(j:0\sim 2^n-1\) 更优。

for(int j=(i-1)&i;j!=i;j=(j-1)&i){...}

枚举 \(k:1\sim n\) 表示 \(n\) 个房间,若状态 \(i\) 和状态 \(j\) 在第 \(k-1\) 位上不同,则表示要新开通第 \(k\) 间房间,为花费累加 \(\operatorname{road(j,k)}\) 。开一个 vector<pair<int,int>> nxt 存储 nxt[j].push_back({i,cost}) ,表示状态 \(j\) 的下一个状态为 \(i\) 且花费为 \(\operatorname{cost}\)

最后计算答案,枚举 \(i:1\sim n-1\) 表示层数(即深度),然后枚举 \(j:0\sim 2^n-1\) 表示 \(2^n\) 种状态,遍历每种状态 \(j\) 的每个 nxt[j] ,更新答案。

P6435 「EZEC-1」数列

传送门

首先需要注意到一个事实,如果第 \(k\) 行的数列实际上是公差为 \((a+b)^{k-1}\) 的等差数列,由样例可以得到印证。

\(f_n\) 表示第 \(n\) 行数列的首项,由题意给出递推式: \(f_n=af_{n-1}+b(f_{n-1}+(a+b)^{n-2})+c=(a+b)f_{n-1}+b(a+b)^{n-2}+c\) 。注意到数据范围,考虑用矩阵加速,时间复杂度为 \(O(\log n)\)

\(\begin{aligned}\begin{bmatrix} f_{n} & b(a+b)^{n-1} & c\\ 0 & 0 &0 \\0 &0 &0\end{bmatrix}&=\begin{bmatrix} f_{n-1} & b(a+b)^{n-2} & c\\ 0 & 0 &0 \\0 &0 &0\end{bmatrix}\times\begin{bmatrix} a+b & 0 & 0\\ 1 & a+b &0 \\1 &0 &1\end{bmatrix}\\&=\begin{bmatrix} f_1 &b& c\\ 0 & 0 &0 \\0 &0 &0\end{bmatrix}\times\begin{bmatrix} a+b & 0 & 0\\ 1 & a+b &0 \\1 &0 &1\end{bmatrix}^{n-1}\end{aligned}\)

特别地,本题中卡乘法精度,要用龟速乘。

P1409 骰子

传送门

\(f_{n,m}\) 表示队列中有 \(n\) 个人时,排在第 \(m\) 个的人获胜的概率。

显然有 \(f_{1,1}=1\) 和方程 \(\begin{aligned}f_{i,j}=\left\{\begin{matrix} &\frac{1}{2}f_{i,i}+\frac{1}{6}& i\gt j=1\\ &\frac{1}{2}f_{i-1,j}+\frac{1}{3}f_{i-1,j-1}&i\ge j\gt 1 \\ \end{matrix}\right. \end{aligned}\)

注意到这里的变量会重复,不可以直接递推求解,如果考虑高斯消元的话,复杂度显然不可以接受。但可以注意到:

\(\begin{aligned}f_{i,1}&= \frac{1}{2}f_{i,i}+\frac{1}{2}\times\frac{1}{3}\\ f_{i,2}&=\frac{1}{2^2}f_{i,i}+\frac{1}{2^2}\times\frac{1}{3}+\frac{1}{3}f_{i-1,1}\\ f_{i,3}&=\frac{1}{2^3}f_{i,i}+\frac{1}{2^3}\times\frac{1}{3}+\frac{1}{2}\times\frac{1}{3}f_{i-1,1}+\frac{1}{3}f_{i-1,2}\\\cdots\\ f_{i,i}&=\frac{1}{2^{i}}f_{i,i}+\frac{1}{2^i}\times\frac{1}{3}+\frac{1}{3}\sum^{i-1}_{j=1}\frac{f_{i-1,j}}{2^{i-j-1}}=\frac{2^i}{2^i-1}\times \frac{1}{3}( \frac{1}{2^{i}}+\sum^{i-1}_{j=1}\frac{f_{i-1,j}}{2^{i-j-1}})\end{aligned} \)

于是我们可以枚举 \(i\) 递推得 \(f_{i,i}\) ,然后代入 \(f_{i,i}\) 解得 \(f_{i,1}\) ,再递推得到所有的 \(f_i\) ,时间复杂度为 \(O(n^2)\)

P6433 「EZEC-1」出题

传送门

注意到会有两种情况:时间可以全做和只能部分做。考虑全做的时候舍弃到价值最小的任务即可。考虑部分做的时候,因为一定会有任务无法做,所以可以忽略舍弃的限制。

\(f_{i,j}\) 表示在还有 \(i\) 个单位时间,且已经选择了 \(j\) 次翻倍时的最大价值。于是有方程:\(\begin{aligned}f_{i,j}= \max(f_{i,j},f_{i-x_T,j}+a_T,f_{i-x_T,j-1}+2\times a_T) \end{aligned}\) ,分别表示:不选择任务 \(T\) ,选择任务 \(T\) 但不翻倍和选择任务 \(T\) 且翻倍。值得注意的是,枚举 \(i\)\(j\) 时要倒序枚举,避免出现重复,同时, \(f_{i,0}\) 需要特殊处理。

P4381 [IOI2008] Island

传送门

讲实话这题超出了我的能力。

一句话题意:求出基环树森林上的直径之和。

我们先考虑对于一棵基环树上的直径应该怎么求。显然有两种情况,分别是在不经过环仅在某一棵子树上或者经过环的两棵子树上。考虑前者很简单,就当做是树的直径去做就可以了;后者就比较麻烦了,显然会是两棵子树在环上的点走向子树内最远的点然后再加上环的距离。

首先要区分出若干棵基环树,染色即可。然后要找出一棵基环树上的环,可以用 \(\operatorname{dfs}\) 或者拓扑排序来做,于此同时可以求出子树内不经过环的最长直径。

然后对于每棵基环树,预处理出环上每个点到其子树上的最长距离。破环成链,可以用单调队列维护环上最远距离。特别要注意二元环的情况。

P8148 声海 | Sea of Voices

传送门

首先将读入的数组 \(p\) 升序排序,显然 \(a_1=p_1,a_2=p_2\) 。然后我们注意到 \(a_1+a_2\) 也在 \(p\) 中出现了,若在 \(p\) 中删除 \(a_1\) , \(a_2\)\(a_1+a_2\) ,则 \(p\) 中最小值即是 \(a_3\) ,同理,再删去 \(a_3\)\(a_2+a_3\)\(a_1+a_2+a_3\) 后, \(p\) 中最小值即是 \(a_4\) 。故我们需要维护的即是这样一个过程。

我们可以用小根堆 \(Q\) 来动态维护 \(p\) 中待删除的值。记一个指针 \(u\) ,若当前 \(Q\) 非空且 \(p_u=Q.top\) ,则需要右移指针 \(u\) 并将堆顶弹出。这保证了当前指针指向的 \(p_u\) 就在 \(a\) 数组中,语文需要删除的值已经随着指针右移被删除了。故我们枚举 \(i:1\sim n\) ,对于每个 \(i\) 先执行上述操作得到的 \(p_u\) 即为 \(a_i\) ,然后将 \(a_i\) 当前的后缀和推入 \(Q\) 中以待删除。

P8149 泪光 | Tears

传送门

很抽象的翻译题。这里给出操作的形式化含义:

操作一:若有 \(v_a=v_b\) ,则保证有 \(v_c=c_d\)
操作二:保证有 \(v_a=v_b\)

值得注意的是,根据题意,每次询问需要考虑历史的操作,换言之,如果经过某次操作使得某个 \(v_a=v_b\) 后,可能会引发连锁使得其他的 \(v_c=v_d\) 成立。考虑如果直接拿并查集做的话,根据上文所述,每次进行操作一时都要先遍历之前未进行的操作来更新并查集,这将花费 \(O(n)\) 的时间,不可接受。

声明:我们称连通块 \(x\) 表示 \(x\) 所在的连通块。同时,认为连通块 \(x\) 的大小表示连通块 \(x\) 尚未执行的操作个数,区别于 \(sz_x\)\(sz_x\) 表示连通块 \(x\) 中的元素个数。

考虑用启发式合并来做,每次存储尚未进行的操作,在后续操作时更新。我们可以用 set[x] 来存连通块 \(x\) 尚未执行的操作,存的时候不妨对连通块 \(a\) 和连通块 \(b\) 都存这个操作,这样在启发式合并的时候就可以通过 set[x].size() 来判断连通块大小了。同时可以开一个标记数组,如果执行了第 \(i\) 个操作一,则标记 \(i\) 避免之后重复去做。对于每个可以执行的操作,可以开一个队列来存储当前的操作序列。

在启发式合并连通块 \(x\) 和连通块 \(y\) 时,不妨认为连通块 \(x\) 的大小更大。遍历 set[y] ,如果当前的操作 \(cur\) 没有被执行过,则判断 \(a_{cur}\)\(b_{cur}\) 是否分别在连通块 \(x\) 和连通块 \(y\) 内,如果在,则标记操作 \(cur\) 并在 set[x] 中删去操作 \(cur\) ,同时将 \(c_{cur}\)\(d_{cur}\) 加入队列中待之后操作。然后再遍历一次 set[y] ,将尚未执行的操作合并入 set[x] 中。最后再合并连通块 \(x\) 和连通块 \(y\) ,因为如果在一开始就合并会使得连通块 \(x\) 和连通块 \(y\) 变成同一个连通块而出现问题。总的时间复杂度为 \(O(n\log^2n)\)

P7263 Something Comforting

传送门

妙妙题。首先题面给出的生成代码能随机生成一串括号序列,将其中不合法的区段反转,可以证明将所有上述区段反转后,就是合法的括号序列。

显然可以注意到随机生成的方案总数为 \(\begin{aligned}\binom{2n}{n}\end{aligned}\) ,于是我们只需要考虑能得到给定合法序列的原序列有多少种。

以样例 \(2\) 为例:合法序列为 ()(()) ,而原序列可能有 \(4\) 种,分别为 ()(()))((())()))(()())(( 。首先可以注意到,我们能将合法序列改写为 ()() 的形式,即仅考虑最外层的括号。因为可以发现,如果括号序列内部有不可法的括号对,即比如 (...) 内有 )( ,则这就会与最外层的括号构成一个合法的序列。通俗地说, ()()() 本身就是合法的,不会再反转变为 ()(()) 。而要想得到一个合法的形如 (...) 的括号对,只可能是其本身或从该括号对完全相反的对反转而来。

故我们可以将合法序列划分为 \(k\) 个形如 (...)(...)...(...) 的括号对,考虑每个括号对都可能由一个合法对或者不合法对转移来,故共有 \(2^k\) 种可能的原序列。答案即为 \(\begin{aligned}\frac{2^k}{\binom{2n}{n}}\end{aligned}\)

P9744 「KDOI-06-S」消除序列

传送门


\(O(qn^2)\) 做法:

不知道为什么这个唐氏做法能拿 45pts ,过了 1~6,8 的点。

\(\begin{aligned}S_i=\sum^{i}_{j=1}b_j\end{aligned}\) ,这可以提前预处理一下。记 \(s_{i}=s_{i-1}+[i\in P]\times c_i,t_{i}=t_{i-1}+[i\in P]\times b_i\) ,分别表示将 \(\left\{x|x\in P \wedge x\in[1,i] \right\}\) 的点由 \(0/1\to1/0\) 所需的代价, \(T_i=S_i-t_i\) 表示将 \(\left\{x|x\notin P \wedge x\in[1,i] \right\}\) 的点由 \(1\to0\) 所需的代价, \(f_i\) 表示将 \(1\sim i\) 更改为合法状态的最小代价。然后显然就得到了一个很丑的转移方程: \(\begin{aligned}f_i=\min(f_{i-1}+[i\notin P]\times b_i,a_i+s_i,\min^{i-1}_{j=1}(f_j+T_i-T_j))\end{aligned}\)


\(O(qn)\) 做法:

不知道为什么这个唐氏做法能拿 60pts ,过了 1~12 的点。

考虑优化 \(O(qn^2)\) 的做法,显然瓶颈在于枚举 \(j\) 。而不难发现,\(\begin{aligned}\min^{i-1}_{j=1}(f_j+T_i-T_j)=T_i+\min^{i-1}_{j=1}(f_j-T_j)\end{aligned}\) 。故记 \(M_i=\min(M_{i-1},f_j-T_j)\) ,就有 \(\begin{aligned}f_i=\min(f_{i-1}+[i\notin P]\times b_i,a_i+s_i,T_i+M_i)\end{aligned}\)


\(O(\sum m)\) 做法:

可以通过本题。

可以发现上文提到的做法在标记 \(P\) 中的每个点时要遍历 \(i:1\sim n\) ,这太局限了,于是要换一种思路,考虑换一种思路,直接枚举 \(i:1\sim m\)。记 \(p_i\in P\)\(f_i\) 表示将 \(1\sim p_i\) 从全 \(1\) 更改为合法状态的最小代价, \(g_i\) 表示将 \(1\sim p_i\)\(0\) 更改为合法状态的最小代价, \(S_i\) 仍为上文所定义。

声明:认为 \(\begin{aligned}\sum^{r}_{i=l}x_i=0(l>r)\end{aligned}\)

首先,可以最小化一下 \(a_i\) ,注意到给某个前缀都更改为 \(0\) 后手动更改后缀的 \(0\) 可能更优,故有 \(\begin{aligned}a_i=\min_{j=1}^{i}(a_j+\sum^{i}_{k=j+1}b_j)=\min(a_i,a_{i-1}+b_i)\end{aligned}\) 。同时,显然有转移方程: \(\begin{aligned}g_i&=g_{i-1}+c_{p_i}\\f_i&=\min(f_{i-1}+\sum^{p_i-1}_{j=p_{i-1}+1}b_j,a_{p_i-1}+g_{i-1})=\min(f_{i-1}+S_{p_i-1}-S_{p_{i-1}},a_{p_i-1}+g_{i-1})\end{aligned}\)

值得注意的是, \(p_m\neq n\) 时要保证后缀为 \(0\) ,类似 \(f_i\) 的转移,故答案为 \(\min(g_m+a_n,f_m+s_n-s_{p_m})\)

P8817 [CSP-S 2022] 假期计划

传送门

感觉是一道还算有趣的题。

由于两点间距离为 \(1\) ,故可以用 \(\operatorname{bfs}\) 来求出任意两点间距离,时间复杂度为 \(O(n^2)\) 。于是我们就可以判定转车次数了,按照题意,需有 \(k+1\ge dis(u,v)\)

考虑如果直接枚举符合条件的 \(A,B,C,D\) 将是 \(O(n^4)\) 的,不可接受。注意到路径为 \(1\to A\to B\to C\to D\to 1\) ,类似 \(\operatorname{meet\, in\, the\, middle}\) 的想法,可以拆分为 \(1\to A\to B\)\(1\to D \to C\) 的两条路径,然后判断 \(B\to C\) 是否合法。故我们可以先预处理合法的 \(1\to u\to v\) 的路径,开一个 set[v] 存储满足条件的 \(u\) 。由于后续还是会需要枚举 \(B,C\) 及合法的 \(A,D\) ,所以这里不能全都存储,而是存储点权前 \(3\) 大的,因为对于 \(B\) 而言,可能满足条件的点同时有 \(A,C,D\) 。于是总的时间复杂度为 \(O(n^2|S|^2)\) ,其中 \(|S|=3\)

P8818 [CSP-S 2022] 策略游戏

传送门

感觉逆天的是我,不是这道题。因为用了 log(r-l+1) 导致调试了一个晚上,交了 \(10\) 发 0pts 代码上去,难绷。警钟敲烂啊!!!

题目挺无脑的,看眼就懂了。根据博弈论里类似纳什均衡的知识,可以注意到最终答案为每行中的最小值在每列中的最大值。

根据样例不难发现,可能要维护的值有最大值、最小值、正数最小值、负数最大值和是否有零。因为懒得分类讨论了,所以我选择这些都求出来,开十个 \(\operatorname{ST}\) 表即可。然后各求出来的五个值用 \(5\times5\) 的循环遍历一下求答案即可。值得注意的是,因为五个数中的后三个值可能不存在,若不存在,则用最大值代替即可,显然不会影响结果。当然可以分类讨论一下,然后少维护几个值,但没必要。

P5590 赛车游戏

传送门

见证 lym \(\operatorname{dfs}\) 没判是否走过而一车 MLE 的题,但感觉这不算坑点啊。

注意到 \(dis_v=dis_u+w\) ,即 \(dis_v-dis_u=w\in[1,9]\) ,考虑差分约束即可。注意到有些边并不在 \(1\to n\) 的路径上,再加之题目保证没有重边,故这些边不会对路径长度造成影响,随便赋一个 \([1,9]\) 的值即可。判断是否必经的做法和 ARC092F(题解在下文)类似但明显更简单。无解的情况是 \(1\to n\) 的路径不存在(lym 因为没判这个导致 WA on #20 and #30 ,警钟长鸣),或者差分约束系统中出现了负环。

P3320 [SDOI2015] 寻宝游戏

传送门

对于每次询问考虑有点集 \(S\) ,显然题目要求的是动态的生成树,记 \(dis(u,v\)) 表示 \(u\to v\) 的简单路径上的权值和,记点集 \(S\) 中有点 \(a_1,\cdots,a_k(k=|S|)\) ,则权值和为 \(\begin{aligned}\sum_{i=1}^{k-1}dis(a_i,a_{i+1})+dis(a_n,a_1)\end{aligned}\) ,此时恰好每条边都经过了两次。显然在树上 \(dis(u,v)=dis(1,u)+dis(1,v)-2\times dis(1,\operatorname{lca(u,v)})\)

于是我们就要考虑动态地维护点集 \(S\) ,首先想到我们可以按照 \(\operatorname{dfn}\) 给点集 \(S\) 排序,这样可以保证两点间距离都是最短的,然后遍历点集 \(S\) 就能计算出权值和。但这样效率太慢了,我们考虑插入和删除带来的贡献。不妨设原先点集 \(S\) 中有点 \(s,t\) ,则权值和为 \(dis(s,t)+dis(t,s)\) 。若新插入点 \(r\) ,不妨设 \(\operatorname{dfn_s}<\operatorname{dfn_r}<\operatorname{dfn_t}\) ,则此时权值和为 \(dis(s,r)+dis(r,t)+dis(t,s)\) ,增量为 \(dis(s,r)+dis(r,t)-dis(s,t)=2\times(dis(1,\operatorname{lca(s,r)})+dis(1,\operatorname{lca(r,t)})-dis(1,\operatorname{lca(s,t)}))\) 。于是我们每次插入时按照 \(\operatorname{dfn_r}\) 找到满足 \(\operatorname{dfn_s}<\operatorname{dfn_r}<\operatorname{dfn_t}\) 的点 \(s\)\(t\) ,每次修改权值和即可。

P4606 [SDOI2018] 战略游戏

传送门

对于每次询问考虑有点集 \(T\) ,显然题目要求的是满足删去后能改变点 \(s\) 和点 \(t\) 连通性的点 \(r\) ,其中, \(s,t\in S,r\in T,S\cap T=\emptyset\) 。不妨先考虑 \(|S|=2\) 时的简化情况。不难想到要求点双联通分量,以此建圆方树,此时的点集 \(T\) 即路径 \(s\to t\) 上的所有点(除点 \(s,t\) )的集合。又因为圆方树上是圆点和方点间隔排列,记 \(dis(u,v\)) 表示 \(u\to v\) 的简单路径上的点数,所以显然 \(|T|=\left \lceil (dis(s,t)-2)/2 \right \rceil\)

然后考虑推广到一般的情况。改记 \(dis(u,v)\) 表示 \(u\to v\) 的简单路径上的圆点数。我们从点 \(1\) 开始 \(\operatorname{dfs}\) ,使得每个点 \(u\) 的点权为 \(dis(1,u)\) 。本题中要求的实际上是满足点集 \(S\) 中的点连通的最小点集(不含点集 \(S\) 中的点),类似上一题,我们可以先计算出每两点间的圆点个数,同样按照 \(\operatorname{dfn}\) 给点集 \(S\) 排序,记点集 \(S\) 中有点 \(a_1,\cdots,a_k(k=|S|)\) ,则 \(\begin{aligned}|T|=\frac{1}{2}(\sum_{i=1}^{k-1}dis(a_i,a_{i+1})+dis(a_n,a_1))-|S|\end{aligned}\) 。特别地,如果 \(\operatorname{lca(a_1,a_k)}\) 是圆点,则要给 \(|T|\) 再加一。其实计算时我们可以看做是在算点 \(u,v\) 间的边数,若有方点则忽略方点,则找到其父亲节点,也即圆点。

P3831 [SHOI2012] 回家的路

传送门

挺好玩但小清新的分层图。显然我们可以每个格点都存,但这样对于空间和时间来说都不能接受,不难发现,其实答案路径的每次转折点都可以认为是在换乘点(当然实际上就是),故而只需要存储换乘点和起止点即可。

然后考虑连边,综上所述,我们可以给每个换乘点编号,给两两换乘点之间连边,当然前提是这两个换乘点的横坐标或者纵坐标相同。另外,根据题意,连边还需要兼顾水平和竖直两种方向。

P9549 「PHOI-1」路虽远

传送门

注意到这里的变量有当前遍历的点的编号,是否闯黄灯和是否限速。如果只有其中的一个或两个变量,则我们可以考虑分层图最短路。但此时显然不行,我们可以考虑 DP 。

\(f_{i,j,k}\) 表示表示到点 \(i\) 闯黄灯 \(j\) 次且不限速 \(k\) 次时所需的最短时间。那么根据题意,我们在做 \(\operatorname{Dijkstra}\) 时转移 \(f_{i,j,k}\) 即可。注意需要确保最多闯黄灯 \(g\) 次,最多不限速 \(m-k\) 次以及需要补齐等待时间。

在此特别说明最多不限速 \(m-k\) 的原因。注意到一共只有 \(m\) 条边,显然在某种合法状态下,一条边不可能会经过第二次,因为不会优于只经过一次的情况,哪怕等也至多会等到同一时间再走。其次注意到有 \(k\) 条边被限速了,换言之在最终答案的路径中,经过的不限速的路径数量取值为 \([0,m-k]\) 。故而我们也发现在本题中不可以枚举 \(k\) 次限速,因为有可能经过了小于 \(m-k\) 条不限速的边而比恰好经过 \(m-k\) 条限速的边优的情况,而只记录限速了 \(k\) 次不能得到这些信息。

P8249 模法问题

传送门

首先注意到循环节是 \(a\times b\) ,记 \(f(x)=x\mod a+x\mod b\) ,即有 \(f(i)=f(i+a\times b)\) 。于是考虑如果区间长度大于 \(a\times b\) ,就可以完全覆盖一整个循环节,故答案就是其中的最大值。反之,则可以对 \(l\)\(r\) 都先对 \(a\times b\) 取模,若取模后 \(l'\le r'\) ,则说明此时的答案在 \([l',r']\) 中取得,反之,则说明此时的答案在 \([1,r']\)\([l',n]\) 中取得。注意到 \(a\times b\) 的范围只有 \(10^6\) ,故可以用 \(\operatorname{ST}\) 表预处理。

P8776 [蓝桥杯 2022 省 A] 最长不下降子序列

传送门

首先注意到插入的 \(k\) 个数可以在一段的最长不下降子序列前或后,但这样还不是最优的,我们可以将插入的操作放在两段最长不下降子序列中间,这样就最优了,当然,还是需要保证两端点不下降。

考虑怎么转移,那我们可以设 \(f_i\) 表示以 \(i\) 结尾的最长不下降子序列,设 \(g_i\) 表示以 \(i\) 开头的最长不下降子序列,这样就方便我们合并了。故答案就是 \(\max(f_i+g_j+k)(j\ge i+k+1 \operatorname{and} a_j\ge a_i)\)

显然求最长不下降子序列的过程可以用数据结构优化做到 \(O(n\log n)\) ,于是只要正着做一遍和倒着做一遍就能求出 \(f_i\)\(g_i\) 了。然后只要将所有 \(g_i\) 都放入一个新的线段树中,枚举前一段序列的右端点 \(i:1\sim n-k\) ,每次查询 \([a_i,V]\) 中最大的 \(g_0\) 即可。由于后一段序列的左端点随着 \(i\) 的移动而移动,于是还有再删除该点即可。值得注意的是, \(a_i\) 的值可能相等,要记录出现次数。

P4060 [Code+#1] 可做题

传送门

首先注意到,显然可以在一段连续区间的后面异或该段区间的异或和来使该位的异或和为 \(0\) ,故右端点不会对答案产生影响。然后我们考虑在一段区间的前面填上一个数来最小化该段区间的异或和。

我们不妨统计区间前缀异或和的某一位上为 \(1\) 的个数为 \(k\) ,以及该段区间的长度为 \(len\) ,则该段区间(包含新填的左端点)对答案在该位上的贡献是 \(\min(k,len-k+1)\) 。前者表示在左端点处填 \(0\) ,故 \(1\) 的个数不变,就会有 \(k\) 的贡献;后者表示在左端点处填 \(1\) ,故 \(0,1\) 的个数反转,就会有 \(len-k+1\) 的贡献,特别地,这里的加一表示的是左端点处的贡献。值得注意的是,如果一段区间的开头是 \(1\) ,那么认为其左端点的每一位填的都是 \(0\)

noi.ac

#3290. Mancala 游戏

传送门

又丑又长的离谱题,这里有 形式化题面

声明:我们称按题意读入的数组的左边为前面,右边为后面。

注意到每次操作只会对前面的部分产生影响,以及所有能操作的位置最终一定会被操作。于是我们不妨倒着考虑。记 \(f_i\) 为每个位置被操作了几次,由后向前计算。

每次后面的操作会让这个位置的数加 \(1\) ,同时由题意,如果在某个位置 \(i\) 满足 \(a_i=i\),则我们肯定可以优先操作这个位置。所以它一共可以操作的次数等于 \(a_{i}\) 加上后面的操作次数再除以 \(i\) 下取整。

最后值得注意的是,题目要求的是最大化操作数,所以注意棋盘非空时还可以额外进行一次操作,然后耗尽操作数。

#3386. 树计数

传送门

诈骗题。注意题目中说的有向简单路径中的逆序对数,于是从两头来看该路径的贡献分别是正序对数加逆序对数。因为本题保证 \(a_i\) 为排列,所以没有相等的情况,故该路径的贡献也就是该路径的对数。记该路径上有 \(n\) 个点,如果称 \(l=n-1\) 为该路径长度,则对数\(l(l+1)/2\) 。故本题答案为 \(\begin{aligned}\frac{1}{2}\sum_u\sum_v[ dis(u,v)^2+dis(u,v)]\end{aligned}\)

考虑用换根 dp 来做。记 \(f_{u,1/2}\) 表示点 \(u\) 到以 \(u\) 为根的子树内的所有点的距离和和距离平方和,记 \(g_{u,1/2}\) 表示点 \(u\) 到以 \(u\) 为根的子树外的所有点的距离和和距离平方和,记 \(sz_u\) 表示以 \(u\) 为根(含点 \(u\) )的子树大小。

容易得到如下方程:

\(\begin{aligned}f_{u,1}&=\sum _{v\in son}(f_{v,1}+sz_v)\qquad f_{u,2}=\sum_{v\in son}(f_{v,2}+2\times f_{v,1}+sz_v)\\g_{u,1}&=g_{fa,1}+f_{fa,1}-f_{u,1}+n-sz_u-sz_u\\&=g_{fa,1}+f_{fa,1}-f_{u,1}+n-2\times sz_u\\ g_{u,2}&=g_{fa,2}+f_{fa,2}-f_{u,2}-2\times f_{u,1}-sz_u+n-sz_u +2\times(g_{fa,1}+f_{fa,1}-f_{u,1}-sz_u)\\&=g_{fa,2}+f_{fa,2}-f_{u,2}+2\times(g_{fa_,1}+f_{fa,1})+n-4\times(f_{u,1}+sz_u)\end{aligned}\)

由于每条路径都被重复计算了一次,故 \(\begin{aligned}\sum_{u} (f_{u,2}+g_{u,2}+f_{u,1}+g_{u,1})=2\sum_u\sum_v[ dis(u,v)^2+dis(u,v)]\end{aligned}\)

但本题还有其他很简单的一次 dfs 做法,可惜我看不懂。特别地,如果没有保证 \(a_i\) 为排列,那么对于相等的 \(a_i\) 需要建虚树,在虚树上 dp ,显然,我也不会。

#3410. 百日草

传送门

首先注意到要最小化疲劳值,而疲劳程度又是一段路径上的最大权值,那么不难想到要二分去做。于是我们可以二分答案,将问题转化为检验对于一个给定的疲劳程度问能否在不超过该上限的情况下从点 \(1\) 走向点 \(n\) 。二分的上限可以证明是 \(3e5\times 1e9=3e14\) ,因为本题的路程实际上也就是洪水泛滥的过程,换言之如果有合法解,就一定能在 \(n-1\) 步内到达点 \(n\)

检验很简单,用两种写法,一种是 queue 写法,一种是 vector 写法,实际上也还是一个思路。

前者其实就是洪水泛滥,通过 \(\operatorname{bfs}\) 来计算题中的 \(i\) ,实际上也就是走的距离。枚举当前点的所有出边,如果指向的点没有被访问过且满足限制,就标记该点并将该点加入队列。最后判断 \(n\) 号点是否被访问过即可。

后者的做法也很简单,枚举 \(i:1\sim n-1\) 表示可能的步数,开一个滚动的 vector 表示当前点和后续点。遍历当前点中的所有点的所有出边,如果指向的点没有被访问过且满足限制,就标记该点并将该点加入后续点的 vector 中。每枚举一步就清空将后续点作为当前点,然后清空后续点。最后判断 \(n\) 号点是否被访问过即可。

#3441. 路径

传送门

赛时没过大样例去问了 lyx ,结果他说是他出的题,呃呃。他的做法是倍增,我写的是线段树+树剖。事实证明我的做法也是对的。

首先不妨考虑链上的情况怎么做,即:每次询问给定区间 \([l,r]\) ,要求最大化 \(a_i-a_j\) ,其中 \(l\le i\le j\le r\)

注意到可以用线段树维护答案。对于一个节点,我们记录其区间最大值,区间最小值和该区间的答案。因为没有修改操作,所以只要考虑合并怎么写,显然答案为左儿子的答案或右儿子的答案或左儿子中的最大值减去右儿子中的最小值三者中的最大值。注意到在建树的时候只合并了各个区间内的答案,而查询的时候需要额外考虑区间与区间之间的关系,即如果 \(L\le mid \lt R\) ,则要跨区间地计算答案,其余的就和普通的线段树一样。

然后考虑怎么放到树上,其实很简单,直接上树剖就行。考虑树剖能将区间拆成了 \(\log\) 段,于是要考虑段与段之间的答案,这时只需要额外维护先前段内的最小值,每次比较一下当前段内的最大值和先前段内的最小值之差是否大于答案,同时尝试用当前段内的最小值更新先前段内的最小值即可。

总的时间复杂度为 \(O(n+q\log^2 n)\) ,虽然全篇都是暴力的数据结构,但跑得速度也不慢多少。另外, std 是倍增做法,时间复杂度为 \(O(n\log n+q)\) 。不过可能数据强度较大的话还是能卡掉树剖的,毕竟如果这种题强制在线且带修才需要用树剖。

#3421. A

传送门

首先注意到每个优惠券都可以优惠前缀的 \(a_i\) 个物品,故我们不妨记 \(D_i\) 表示第 \(i\) 个物品所能用的优惠券额度之和,此时我们改称优惠额度为 \(D_i\)

考虑这样两种策略:

  • 选最小的 \(k\) 个,并用其中的最大优惠额度。
  • 选最小的 \(k-1\) 个,并用此外的最大优惠额度。

\(rk_i\) 表示数组 \(c\) 中第 \(i\) 小的位置,记 \(\begin{aligned}t=\min_{i\le i\lt k}rk_i\end{aligned}\) 。我们对上述策略分别计算答案,取其中较小者即可。注意到 \(D_i\)递减的,故前者的答案为 \(\begin{aligned}\sum^{k}_{i=1}c_{rk_i}-D_{\min(t,rk_k)}\end{aligned}\) ,后者的答案为 \(\begin{aligned}\sum^{k-1}_{i=1}c_{rk_i}+\min_{1\le i\lt t}(c_i-D_i)\end{aligned}\)

由于 \(c_i-D_i\) 的大小不太直观,在此简单证明一下后者此外的最大优惠额度一定在 \([1,t-1]\) 中取得。不妨记 \(\begin{aligned}c_p-D_p=\min_{1\le i\lt t}(c_i-D_i)\end{aligned}\) ,假设 \(\exist q\ge t\) 使得第 \(q\) 个物品不在最小的 \(k-1\) 个物品(即 \(c_q>c_p\) )满足 \(c_p-D_p\gt c_q-D_q\) 。移项得 \(c_p-c_q>D_p-D_q>0\) ,即有 \(c_p>c_q\) ,矛盾。

2023省熟中集训

T363961 2023省熟中集训#1 E 独白

传送门

这题是21年我自己随便组了场情人节欢乐赛的压轴,当时找到了 lyx 来帮忙出了道题。但由于一直没写这题而且马上又要把这题用在校内模拟赛里,那还是来写一写吧。于是简单学习了一下 dsu on tree。

注意到题目中给出的属性间的限制,可以考虑用并查集来维护。每个 \(c_i\) 各是一个连通块,每对 \(x_i\)\(y_i\) 间同样连通。于是不妨将每个颜色换成其所在并查集的根,于是就变成了 \(m\) 个询问,每次询问一个子树的不同颜色的个数 \(cnt\)

考虑离线。预处理出以每个节点为根的不同答案。直接做是 \(O(n^2)\) 的,但显然这个统计的过程是可合并的,所以可以考虑 dsu on tree ,于是便可在 \(O(n\log n)\) 的时间内计算出答案。最后,输出的是 \(k^{cnt}\) ,总的来说是一道板子题。

*T363959 2023省熟中集训#1 F 规划

传送门

不难注意到一个性质,即如果要通过区间 \([L_i,R_i]\) 中的某一条边。不妨设 \(u,v,w\in[L_i,R_i]\) ,则称这条边为 \(u\to v\) 。为使路程最短,那么显然不会走回头路,即不会有 \(u\to v\to w\) 的情况出现,因为此时 \(u\to w\) 是更优的。

本题的瓶颈为连边所需的时间,我们需要一个能做到 \(O(n)\) 连边的算法。利用上述性质, \(\forall i\in [1,n]\) ,我们可以建一个边权为 \(0\) 的反向边,即令 \(i+1\to i\) 。此时便可以做到题意中所说的,在 \([L_i,R_i]\) 间的任意两点连一条长度为 \(W_i\) 的边了。

但此题还有更优的做法。待补。

T373740 2023省熟中集训#8 B Remmuk 定理

传送门

Legendre 在 1808 年提出 \(n!\) 中含有的素数 \(p\) 的幂次为 \(\sum_{i\ge1}\left \lfloor n/p^i \right \rfloor\)

本题的一个不严谨的证明:

\(n=k_1p+b_1\) ,则 \(\left \lfloor n/p \right \rfloor\) 实际上取出了 \(n!\)\(p\) 的所有的系数 ,即是 \(\left \{ 1,2,\cdots,k_1-1,k_1 \right \}\)

同理,设 \(n=k_2p^2+b_2\),则 \(\left \lfloor n/p^2 \right \rfloor\) 实际上取出了 \(n!\)\(p^2\) 所有的系数 ,即是 \(\left \{ 1,2,\cdots,k_2-1,k_2 \right \}\) 。以此类推,可以得到答案。

T373741 2023省熟中集训#8 D 序列

传送门

首先不难观察到一个性质,对一个长度为 \([L,R]\) 的区间覆盖其中的最大值,需要的操作数是 \(\left \lfloor (L+R)/2 \right \rfloor\) ,这是这一操作的代价。我们来考虑这一操作的收益,设为 \(w_{l,r}\),显然 \(w_{l,r}=(r-l+1)\times \max_{i=1}^r a_i\)

此时我们知道了一次操作的收益和代价,类似背包问题,我们可以将 \(k=1,2\dots,n\) 视为 \(V\) ,将当前区间枚举到的位置设为 \(i\) ,将从上一个区间转移来的长度设为 \(j\) ,设 \(f_{i,k}\) 表示在现有 \(k\) 次操作数时, \([1,i]\) 的最大权值。于是显然有方程, \(f_{i,k}=\max(f_{i-j+1,k-\left \lfloor j/2 \right \rfloor}+w_{i-j+1,i})\) ,时间复杂度 \(O(n^3)\)

NOIP2022模拟

D9h1o. 欧几里得

传送门

xyx 模拟赛的玄妙题 T1 。

如果棋子不用转向,那么我们是很容易求出其落点的。然后我们考虑,棋子至少会经历一次转向。故落点的范围为 \([T-X,T+Y)\)

不妨设棋子向左移动了 \(A\) 次,则落点坐标为 \(S-A\times X+(N-A)\times Y=S+N\times Y-A\times(X+Y)\)

于是来到了本题最有趣的地方了。注意到,棋子落点的范围对 \(X+Y\) 取模后恰能保证互不相同。而落点坐标对 \(X+Y\) 取模后,就不再有未知数 \(A\) ,也即唯一确定了。于是分三类讨论即可解决本题。

NOIP2023模拟

38123 等差数列

给定两个等差数列,问在某个特定的范围内有多少个数同时出现在两个等差数列中?
给定 \(a_1,a_2\) 作为数列 \(\left \{ a_n \right \}\) 的前两项,\(b_1,b_2\) 作为数列 \(\left \{ b_n \right \}\) 的前两项,\(l,r\) 表示询问的范围。
\(1 ≤ a_1, a_2, b_1, b_2 ≤ 100\) ,且 \(a_2 ≥ a_1 × 2,b_2 ≥ b_1 × 2,1\le l\le r ≤ 10^{18}\) .

\(\left \{ a_n \right \}\) 的公差为 \(d_a\)\(\left \{ b_n \right \}\) 的公差为 \(d_b\)
注意到仅有两种情况:

  1. 两个数列没有相同的数。
  2. 相同的数将构成一个新的等差数列 \(\left \{ c_n \right \}\) ,并满足其公差 \(d_c\)\(lcm(d_a,d_b)\)

对于情况 \(1\) ,我们可以特判一下即可。

对于情况 \(2\) ,注意到 \(r\) 的范围很小,我们可以开一个桶记录一下某个数是否在 \(\left \{ a_n \right \}\)\(\left \{ b_n \right \}\) 出现,然后将同时出现的数加入一个新的数列 \(\left \{ c_n \right \}\)

显然地, \(c_n=c_1+(n-1)\times d_c\) 。移项可知, \(n=(c_n-c_1+d_c)/d_c\) 。设 $f(x)=\left \lfloor (x-c_1+d_c)/d_c \right \rfloor $ ,表示中 \(\left \{ c_n \right \}\) 有多少数小于等于 \(x\) 。答案即 \(f(r)-f(l-1)\)

38142 传送

\(n\) 个砖块,编号依次为 \(1,2,\cdots,n\) 。初始时你位于编号为 \(n\) 的砖块上,你的目标是到达编号为 \(1\) 的砖块。
如果你位于编号为 \(x(2\le x\le n)\) 的砖块上 ,你有两种操作可以选择:
操作1. 选择一个整数 \(y(1\le y\le x-1)\) ,然后传送到编号为 \(y\) 的砖块上。
操作2. 选择一个整数 \(z(2\le x\le n)\) ,然后传送到编号为 \(\left \lfloor x/z \right \rfloor\) 的砖块上。
试求出你有多少种方法能够从编号为 \(n\) 的砖块传送到编号为 \(1\) 的砖块。将答案对一个质数 \(p\) 取模。
\(1\le n \le4\times10^6,10^8\le p\le10^9\) ,且保证 \(p\) 为质数

对于本题的第一直觉是,从后往前计数,但这样显然不能满足条件。于是需要考虑从前向后计数。记 \(f_i\) 表示从编号为 \(i\) 的砖块传送到编号为 \(1\) 的砖块的方案数。

直接暴力操作是 \(O(n^2)\) 显然,我们考虑优化。

对于操作 \(1\) ,考虑从 \(x\to i\to 1\) ,显然其贡献为 \(\sum^{x-1}_{i=1}f_i\) ,这个只要在枚举 \(x\) 的时候顺带记录一下即可。

对于操作 \(2\) ,第一反应是数论分块,但注意到题目中 \(n\) 的上限,是卡满了根号做法的,那只能考虑有没有单 \(\log\) 做法。

考虑枚举 \(z\) ,则编号在 \([x*z,(x+1)*z-1]\) 内的砖块可以通过参数为 \(z\) 的操作 \(2\) 传送到 \(x\) 。于是对这一编号区间内的砖块 \(+f(x)\) ,此时可以用差分实现 \(O(1)\) 的区间加。由调和级数知时间复杂度为\(O(n\log n)\)

坑点:注意在枚举 \(z\) 时,需应用代码 \(1\) ,代码 \(2\) 和根号做法都会被卡。

for(int z=2;z<=n/x;++z){...}//代码1
for(int z=2;z*x<=n;++z){...}//代码2

Codeforces

*CF1242B 0-1 MST

传送门

显然可以利用类似缩点的思想,将有 \(0\) 权边相连的点缩为一个连通块内,然后计算连通块个数 \(cnt\) ,最小生成树权值即 \(cnt-1\) ,时间复杂度 \(O(n\log n+m)\)

zak 爷有个神仙的线性解法,咋想到的。待补。

CF1483D Useful Edges

传送门

设城市 \(u\) 到 城市 \(v\) 的路径长度为 \(e[u][v]\) ,最短距离为 \(d[u][v]\) ,时间预算为 \(l[u][v]\)

根据题意,如果 \(i \to j\) 为 城市\(u\) 和城市 \(v\) 间的重要路径,则满足 \(d[u][i]+e[i][j]+d[j][v]\le l[u][v]\)

直接枚举 \(u,v,i,j\)\(O(n^4)\) 的,显然不可以接受,于是考虑优化。一般来说,对于满足这样的不等关系的式子,我们可以通过减少未知量来降低复杂度。

在此,我们不妨移项,将上式转变为 \(e[i][j]+d[j][v]\le l[u][v]-d[u][i]\) 。这样可以只进行三方的枚举,就能分别求出不等号两边的量了。

我们可以先枚举 \(u,v,i\) 预处理出不等号的右边,注意是 \(\le\) ,所以要使不等号右边的值最大。然后再枚举 \(i,j,v\) ,判断是否符合条件即可。

CF15C Industrial Nim

传送门

鉴定为“为了这点醋才包的饺子”。

首先注意到这是个 Nim 游戏,不过实际上标题里已经给出了呃呃。那么我们只要考虑 \(\sum{x_im_i}\) 组情况异或起来的答案为 \(0\)\(1\) ,若为 \(1\) 则先手胜;反之则后手胜。

注意到这里 \(1\le x_i,m_i\le 10^{16}\) ,数据范围是 \(10^{16}\) 而非 \(10^6\) !显然如果是后者我们能前缀异或和做,但这里明显不行,于是我们需要注意到一些性质。注意到一个偶数在增加一之后,其二进制位除了最后一位都没有变化,故 \(2k\oplus2k+1=1,k\in \mathbb{N^*}\) 。类似地,我们可以推导出 \(2k\oplus2k+1\oplus2k+2\oplus2k+3=0,k\in \mathbb{N^*}\)

于是我们不难得到 \(\bigoplus^{n}_{i=1}i\)\(n\mod4\) 有关: \(\bigoplus^{n}_{i=1}i=\left\{\begin{matrix} n&iff&n\mod4=0\\ 1&iff&n\mod4=1\\ n+1&iff&n\mod4=2\\ 0&iff&n\mod4=3 \end{matrix}\right.\)

故本题可以以 \(O(n)\) 的复杂度解决。这是个值得一记的trick。

CF915F Imbalance Value of a Tree

传送门

首先注意到,可以将最大值和最小值拆开来计算。其次注意到,可以将点权化为边权。最后注意到,类似 kruskal 地加边满足算出来的是最大/最小权值和,每次加边对答案的贡献为 \(sz_u\times sz_v\times w(u,v)\)

CF1527D MEX Tree

传送门

鉴定为多测没清空调了我一个半小时。有这么一篇 题解 写的很好,我的思路都基于这里。

首先注意到 \(\operatorname{mex}\) 的性质,若在一条路径上要有 \(\operatorname{mex}= i\) ,则要满足这条路径上有 \([0\sim i-1]\) 且没有 \(i\) 。于是我们可以在原树的基础上,维护一棵新的合法路径树,每次动态加入点 \(i\) ,要求加入的点 \(i\) 存在符合 \(\operatorname{mex}=i\) 的路径。

\(ans_i\) 表示符合 \(\operatorname{mex}=i\) 的路径数,记 \(sz_i\) 为以 \(i\) 为根的子树大小。特别地,要先计算出 \(ans_0\)\(ans_1\) 。不难发现 \(ans_0\) 需要路径的两端点都在以 \(0\) 为根的某个子树内,于是有: \(\begin{aligned}ans_0=\sum_{v\in son_0}\binom{sz_v}{2} \end{aligned}\) 。然后考虑 \(ans_1\) ,即是在去掉 \(1\) 的子树后的点集中任意选取两个点产生的路径,减去同时不经过 \(0\) 的路径数量(即两端点仅在以 \(0\) 为根的某个子树内),于是有: \(\begin{aligned}ans_1=\binom{sz_0-sz_1}{2}-\sum_{v\in son_0}\binom{sz_v-sz_1\times[\operatorname{LCA(1,v)=v]}}{2} \end{aligned}\)

一般地,我们考虑怎么计算 \(ans_i(i\ge2)\) ,在此之前,我认为提前介绍怎么动态维护新的合法路径树更好。首先我们考虑用 \(lp\)\(rp\) 分别代表合法路径树的左右拓展的最远的点。这里的 \(lp\)\(rp\) 与原树中其的位置是一一对应的,记录的仅仅是编号。可以证明,合法路径树肯定是二叉树。我们以此来刻画各个合法路径。

在最初时 \(lp=rp=0\) ,表示路径仅为 \(0\) 这一个点。然后我们考虑加入 \(1\) ,在此我们规定\(1\) 所在子树就是左子树,优先拓展左端点,即令 \(lp=1\) 。记 \(x\) 为当前判断是否可以加入的点的编号。个人认为本题还是代码胜过一切注解,于是在此给出代码。

inline bool add(int x){// 判断 x 是否可以加入合法路径树中 
	int lf=LCA(lp,x),rf=LCA(rp,x);
	if(!rp){// 路径有一端点为零(认为是 rp ,因为我们先拓展了左子树),当然最初 lp=rp=0 的情况也考虑在内了 
		if(lf==x)return true;//	当前 x 在路径上
		else if(lf==lp){lp=x;return true;}// 当前 x 在非零路径端点的子孙节点上 
		else if(lf)return false;// 当前 x 出现了分叉,不可以加入 
		else if(!lf){rp=x;return true;}// 当前 x 在右子树内,我们扩展到右子树去 
	}else{// 路径两端点都不为零 
		if(lf==x||rf==x)return true;// 当前 x 在路径上 
		else if(lf==lp){lp=x;return true;}// 当前 x 在非零路径端点的子孙节点上 
		else if(rf==rp){rp=x;return true;} 
	}
	return false;// 当前 x 出现了分叉,不可以加入 
}

我们枚举 \(i\in[2,n]\) ,每次先判断点 \(i-1\) 是否可以加入合法路径树中,如果不可以就退出循环,显然此时 \(\operatorname{mex}\) 不会是 \(i\sim n\) ,故后缀答案都为 \(0\) ;反之,则计算在扩展到 \(i-1\)\(lp\)\(rp\) 下的 \(ans_i\) 。类似 add(x) ,我们可以用一个 work(x) 来计算,这里的 \(x\) 满足在若干条合法路径上有 \(\operatorname{mex}=x\) 。在下面代码中还出现了 \(sp\) ,我们称 \(sp\)\(0\)\(1\) 的儿子所在的子树(也就是以 \(0\) 为根的左子树)大小,这方便我们得到右子树的大小。

inline int work(int x){ // 计算 ans_x
	int lf=LCA(lp,x),rf=LCA(rp,x);
	int res1=0,res2=0;// 分别表示左右子树中可选的点来构成路径的两端点 
	if(!rp){
		res1=sz[lp],res2=sz[rp]-sp;// 减去 sp 后就是在扩展到右子树之前得到右子树(含根节点)的子树大小 
		if(lf==x||rf==x)return 0;// 当前 x 在路径上,显然不能以之为 mex 
		else if(lf==lp)res1-=sz[x];// 当前 x 在非零路径端点的子孙节点上 
		else if(!lf)res2-=sz[x];// 当前 x 第一次出现在了右子树中 
	}else{
		res1=sz[lp],res2=sz[rp];
		if(lf==x||rf==x)return 0;// 当前 x 在路径上,显然不能以之为 mex 
		else if(lf==lp)res1-=sz[x];// 当前 x 在非零路径端点的子孙节点上 
		else if(rf==rp)res2-=sz[x];
	} 
	return res1*res2;// 乘法原理 
}

特别地,如果在枚举 \(i\) 的时候有 \(i=n\) ,那么说明此时的树是一条,显然此时 \(ans_n=1\)

最后记得多测要清空

CF95E Lucky Country

传送门

首先我们可以计算出连通块的大小,然后问题就转变最少合并几个连通块使得能得到 Lucky Number 。

\(f_i\) 表示大小为 \(i\) 的联通块最少需要 \(f_i\) 个初始联通块合并得到,故答案为合并次数,显然就是 \(\min(f_x)-1\) ,其中, \(x\) 是 Lucky Number 。不难发现这个时候就是一个多重背包问题,每个物品的价值为 \(1\) ,重量为其所属连通块大小 \(sz_i\) ,最小化价值即可。直接多重背包肯定不行,可以单调队列优化或者二进制拆分优化,可以证明复杂度是 \(O(n\sqrt n)\)

CF1353F Decreasing Heights

传送门

首先根据贪心策略,肯定存在一种方案是最优的且满足其中的某一点不会被操作。不妨假设 \(a_{1,1}=0\) ,则最终到达某个点经过最少操作后的高度肯定如表所示:

\(0\) \(1\) \(2\) \(3\)
\(1\) \(2\) \(3\) \(4\)
\(2\) \(3\) \(4\) \(5\)
\(3\) \(4\) \(5\) \(6\)

此时注意到一个点 \((x_1,y_1)\) 与点 \((x_2,y_2)\) 之间的高度差满足 \(a_{x_1,y_1}-x_1-y_1=a_{x_2,y_2}-x_2-y_2\) 。换言之,我们只需要知道某点的高度,就能得到该图最终理想的高度情况。

\(f_{i,j}\) 表示从 \((1,1)\) 出发到达点 \((i,j)\) 需要操作的最少次数,显然有方程 \(f_{i,j}=\min(f_{i-1,j},f_{i,j-1})+\Delta H\) 。显然 \(\Delta H\) 可以通过已知的该点推导出来。值得注意的时,如果推导出来的高度大于原本的高度是不合法的。

注意到 \(n\) 的范围很小,我们可以 \(O(n^2)\) 地枚举某个点,认为该点一定不会被操作,然后再 \(O(n^2)\) 地由该点推导出该图上所有点的高度情况,并计算出此时的 \(f_{n,m}\) ,取其中的最小值即可。

CF980E The Number Games

传送门

首先注意到,选第 \(x\) 个点的收益比选择了前 \(1\sim x-1\) 个点的收益还大,故而考虑倒序枚举选取哪些点,根据样例 \(2\) 亦可知,我们要优先满足当前点的需要。

我们不妨认为是在给以 \(n\) 为根的树不断加点,每次需要满足从当前点到根的路径上待加入的点数小于当前可以添加的点数。不妨用树剖维护,对除根外的所有点赋值为 \(1\) ,然后每次查询待加入的点数,如果可行,则将当前点到根的路径都赋值为 \(0\) ,上述操作很容易用树剖实现。最后对每个点查询当前点的点权,若为 \(1\) 则为答案。

有一说一,用 \(\operatorname{C++14}\) 比用 \(\operatorname{C++20}\) 慢了好多啊。

CF226C Anniversary

传送门

挺有趣的一道题。声明: \((a,b)\) 即表示 \(\gcd(a,b)\)

首先有个经典结论: \((F_n,F_m)=F_{(n,m)}\) ,读者自证不难。于是问题就转化为了求出 \([l,r]\) 中满足条件的最大的 \((a_1,\dots,a_k)\) 。这其实也不难,直接 \(O(\sqrt r)\) 枚举即可。显然对于 \(x\) 而言,若 \(\begin{aligned}\left \lfloor \frac{r}{x} \right \rfloor -\left \lfloor \frac{l-1}{x} \right \rfloor \ge k\end{aligned}\) ,则这个 \(x\) 是符合条件的。类似地,还要考虑 \(x'=\left \lfloor \frac{r}{x} \right \rfloor\) 时的情况。

CF1446C Xor Tree

传送门

声明:下文中数字二进制位是从左向右数且 \(\operatorname{1-index}\) ,例如: \((001)_2\) 的第 \(2\) 位和第 \(3\) 位是 \(0\) ,第 \(1\) 位是 \(1\)

考虑对原序列建出一个 \(\operatorname{01-trie}\) ,由于 \(a_i\le 10^9\) ,故建 \(30\) 位使得每个叶子节点都表示每个数的第 \(1\) 位。

根据题意,如果考虑重边的话一共会有 \(n\) 条边,需要得到一棵 \(n-1\) 条边的树,则需要有且仅有一对数 \((a_i,a_j)\) 满足有一条重边。

考虑对于两个在 \(\operatorname{01-trie}\) 上的叶子节点 \(u,v\) 何时会连边(在本段的下文中,简称“叶子节点”为“点”)。显然,若他们的第 \(2\) 位相同,那么他们会连边,且会有一条重边。类似地,如果某两个点 \(p,q\) 的第 \(3\) 位相同,若点 \(p,q\) 都是孤立(指两个点没有兄弟节点)的,那么他们会连边,且会有一条重边;若仅有点 \(p\) 是孤立的,称点 \(q\) 的兄弟节点为点 \(r\) ,则显然点 \(q,r\) 会连边且有一条重边,而点 \(p\) 会有一条边连向点 \(q\) 或点 \(r\) ;若点 \(p,q\) 都不是孤立的,则显然点 \(p,q\) 不会连边。故而我们注意到,只有当两点的第 \(2\) 位相同时,才会有边相连且有一条重边;反之,则会有边相连但没有重边。

我们考虑转化删除最少的点转化为保留最多的点。记 \(f_u\) 表示以 \(u\) 为根的子树内最多保留的点的个。那么,若在 \(\operatorname{01-trie}\) 上点 \(u\) 是叶子结点,则 \(f_u=1\) ;若左儿子或右儿子为空,则 \(f_u=f_v\) ;若左右儿子均不为空,则 \(f_u=\max(f_{v_1},f_{v_2})+1\) ,因为根据上文的分析我们可以知道对于两棵非空的子树仅可保留一个数使得其有边连向最大的连通块。

AtCoder

[ARC137C] Distinct Numbers

传送门

有趣博弈论。

首先注意到这样一个事实,即如果是类似 0 1 _ _ 4 5 这样的状态,中间的空格数为偶数时,显然后手必胜;如果类似 0 1 _ _ _ 5 6 这样的状态,中间的空格数为奇数时,显然先手必胜,因为此时最优策略显然是最大值填补上最右边的空格。

然后考虑,如果是类似 0 1 _ _ 3 _ 5 这样的状态,换言之最大值与次大值不相邻时,显然先手必胜。注意到此时的最大值可以填补在次大值的左或右。如果次大值前的空格数为偶数时,则填补在次大值右侧邻位;如果次大值前的空格数为奇数时,则填补在次大值左侧邻位。注意到这样就能保证是满足上文所提事实的先手必胜策略了。

最后考虑,如果一开始最大值与次大值相邻的情况。那么实际上就变成了上文所提的最优策略,即每次最大值填补上最右边的空格,因为显然如果不直接放在相邻的位置,就会构造出最大值与次大值不相邻的情况,这样显然不优。于是只需要考虑空格数,由 a[n]-n 的奇偶性即可计算出结果。

[ABC098D] Xor Sum 2

传送门

首先注意到一个基本事实, \(a\oplus b\le a+b\) 。于是对于本题而言,要满足区间和等于区间异或和,则要在任意区间都相等。

于是我们可以考虑双指针。每次固定 \(l\) ,每次拓展到最远的 \(r\) ,对于每个区间 \([l,r]\) ,对答案的贡献为 \(r-l+1\) 。由上述性质, \(r\) 的位置是单调不降的。时间复杂度为 \(O(n)\) ,优于二分 \(r\) 的做法。

[ABC290E] Make it Palindrome

传送门

我们称 \((a_i,a_j)(i<j)\) 为一个,如果 \(a_i\neq a_j\) ,那么该会产生的贡献是 \(\min(i,n-j+1)\) ,因为显然我们可以从区间 \([i,j]\) 出发,每次向两侧各扩展一格直到边界,故答案为 \(\begin{aligned}\sum_{1\le i\lt j\le n}[a_i\neq a_j]\times\min(i,n-j+1)\end{aligned}\)

考虑用容斥改写式子: \(\begin{aligned}\sum_{1\le i\lt j\le n}\min(i,n-j+1)-\sum_{1\le i\lt j\le n}[a_i= a_j]\times\min(i,n-j+1)\end{aligned}\)

先考虑每个对所有区间产生的影响,我们可以双指针计算,令 \(l\) 指向左端点, \(r\) 指向右端点。 \(l\) 左侧(包含 \(l\) )的元素个数有 \(x=l\) 个, \(r\) 右侧(包含 \(r\) )的元素个数有 \(y=n-r+1\) 个。对于一个区间 \([l,r]\) ,如果 \(x<y\) ,此时 \(r\in[l+1,r]\) 的情况都能满足 \(x<y\) ,故对答案的贡献为 \((r-l)\times x\) ,然后我们可以右移 \(l\) ;反之,此时 \(l\in[l,r-1]\) 的情况都能满足 \(x\ge y\) ,故对答案的贡献为 \((r-l)\times y\) ,然后我们可以左移 \(r\) 。考虑其正确性,每次我们移动 \(l\) 时,都保证以 \(l\) 为左端点的区间都计算完了,移动 \(r\) 时同理。

类似地,存储一下每个相同数对的坐标,计算每个满足 \(a_i=a_j\)对所有区间产生的影响即可。

[ARC145C] Split and Maximize

传送门

让我觉得很高手的计数题。首先明确题意,是对一个排列 \(P\) 划分两个长度为 \(n\)子序列

为了区分序列 \(A,B\) \((A_i,B_i)\),我们用 \(\left \{\dots\right \}\) 表示序列。

首先考虑对于一个排列 \(P\) ,容易证明其最大值 \(M=\sum^n_{i=1}i(i+1)\) 。若 \(P=(1,2,3,4)\) ,则可以划分出 \(A=\left \{ 1,3 \right \},B=\left \{ 2,4\right \}\) 两个子序列,从而得到 \((1,2),(3,4)\) 为两个\(M=1\times2+3\times4=14\) 。而若 \(P=(1,4,3,2)\) ,则不行,因为此时怎么都无法划分出 \(A=\left \{ 1,3 \right \},B=\left \{ 2,4 \right \}\) 这两个子序列,也就无法得到 \((1,2),(3,4)\) 为两个,故而 \(P=(1,4,3,2)\) 的情况不合法。

注意到本题肯定是直接计数比排除情况更方便。从 \(P=(1,2,\dots,2n-1,2n)\) 出发考虑,此时的\((2k-1,2k)\) ,一共有 \(n\) 个对,而每个对之间交换是不影响的,故其对答案的贡献为全排列 \(n!\) 。其次交换 \((A_i,B_i)\) 对于计算得到一个的值 \(A_iB_i\) 是不影响的,故其对答案的贡献为 \(2^n\)

然后就到了本人一度陷入思维怪圈的环节。我们此时已经考虑了其他合法情况,所以现在假设每个对的先后已经确定了。以 \(P=(1,2,3,4,\dots,2n-1,2n)\) 为例,此时的合法\((1,2),(3,4),\dots,(2n-1,2n)\) 。顺序确定后,考虑一个括号序列和排列的对应关系,假设第 \(i\) 个左括号就对应 \(A_i\) ,第 \(i\) 个右括号都对应 \(B_i\) ,确定了这个之后就相当于括号序列计数,即在任何时刻下,右括号个数不能大于左括号,不然就存在右括号不匹配。故其对答案的贡献即卡特兰数 \(\begin{aligned}H_n=\frac{\binom{2n}{n} }{n+1}\end{aligned}\)

故本题答案为 \(2^n\times n!\times H_n\)

[ABC320F] Fuel Round Trip

传送门

声明,下面出现的 \(x=\min(y)\) 均表示 \(x=\min(x,y)\)

对于这样来回的题目,我们可以枚举分界点,同时计算来时左侧答案和回时左侧答案,这样只需要一次从左向右的枚举就能解决问题。可以设 \(dp_{i,j,k}\) 表示只考虑 \([x_1,x_i]\)
的范围内,去程到 \(x_i\) 时恰有 \(j\) 的油量,回程到 \(x_i\) 时有不超过 \(k\) 的油量时的最小花费。显然 \(dp_{0,H,0}=0\)

注意到 \(N,H\) 同阶且数据范围较小,考虑三方转移,枚举 \(i,j,k\) ,记 \(d=x_{i+1}-x_i\) ,我们用 \(dp_i\) 来更新 \(dp_{i+1}\) 。首先判断边界情况,若 \(dp_{i,j,k}\) 没有被更新过或 $j-d<0 $ 或 \(k+d>H\) 都是不合法的,不用转移。而需要转移的情况一共也就三种,分别是:

  1. 都不加。显然此时 \(dp_{i+1,j-d,k+d}=\min(dp_{i,j,k})\)
  2. 去程加。记 \(v_1=\min(j-d+f_i,H)\) ,显然此时 \(dp_{i+1,v_1,k+d}=\min(dp_{i,j,k}+p_i)\)
  3. 回程加。记 \(v_2=\max(k+d-f_i,0)\) ,显然此时 \(dp_{i+1,j-d,v_2}=\min(dp_{i,j,k}+p_i)\)

最后枚举 \(dp_{n-1,j,k}\) ,记 \(lst=x_{n}-x_{n-1}\) 。若 \(j-lst\ge k\) ,则更新答案,表示在最后一个加油站要有 \(j\) 的油才能保证往返后还有至少 \(k\) 的油。

[ABC077D] Small Multiple

传送门

有一个经典的 trick ,即对于一个数 \(x\) 而言,对其 \(x\) 的数位和产生贡献的可能为 \(x+1\) ,此时数位和加一,或是 \(x\times 10\) ,此时数位和不变。考虑对 \(1\) 重复上述操作,则能生成所有的数。于是在此基础上做模 \(n\) 的同余最短路即可,注意到边权为 \(0\)\(1\) ,故做 \(\operatorname{01bfs}\) 即可。

[ARC092F] Two Faced Edges

传送门

很有趣的一道题。考虑能改变 \(\operatorname{SCC}\) 数量的条件是什么,不妨假设有一条 \(u\to v\) 的路径,则有如下两个条件:

  • 有一条 \(v\to u\) 的路径。
  • 有另一条 \(u\to v\) 的路径。

若上述两个条件其中有且仅有一个满足,则能证明 \(\operatorname{SCC}\) 数量发生了改变,证明显然。于是我们考虑对每一条路径判断上述两个条件是否满足。

前者很简单,对每个点遍历所有边即可。后者其实也不难,若满足了条件,则证明 \(u\to v\) 之间至少有两条路径连结,我们可以先正着遍历 \(u\) 的所有出边,对到达的每个点标记上当前出边的编号。然后再反着遍历 \(u\) 的所有出边,所此时到达的点的标记与当前出边的编号不同,则表明后者成立。

posted @ 2023-08-02 00:34  触情离殇haphyxlos  阅读(73)  评论(0编辑  收藏  举报