Cry_For_theMoon  

1. Count Voting

把 team 相同的分成一组,我们可以做这样一个问题:钦定每个点的出度和入度求连边数。

当然这里我们发现有一些不同方案数的丢失,出现在两部分,入边和出边的分配。

由于我们清楚每一组的构成,每一组里入边的分配是容易计算的,就是 \(\frac{indeg!}{\prod c!}\)

出边的分配考虑在 dp 里做。

这个连边的过程,如果排成一列,非常像线头 dp。

\(f(i,j,k)\) 表示前 \(i\) 个组内部匹配完毕,还剩 \(j\) 个入线头 \(k\) 个出线头的方案数,转移再枚举两层循环,这里的转移系数需要思考一下:首先是 \(i+1\) 为起点的若干条出边,分配出一部分向左剩下向右,这里有一个组合数;另外从 \(k\) 个出线头分配若干个给 \(i+1\),还有未来的若干个出线头分配一些给它,一共有三个组合数系数。

不太会算这个复杂度,但跑的非常快,感觉弗如容斥做法的 \(O(n^2)\)\(O(n\log^2 n)\)

记录

2. A New Beginning

如果一个点在距离人 \(x\) 的时候被打上标记,以它为中心做一个边长 \(2x\) 的正方形,则这个人首先必须落到正方形的边上,且永远不能进入它。

由于可以认为人的行动轨迹是无限长的,那么正方形的左上角或者右下角必定有且仅有一个会被人经过,那就不妨每个点为中心做一个反斜杠的射线,当人被这条射线扫到的那一步给它打标记。

在坐标系上所有这样的斜线画出来,按照斜线为阶段去 dp,然后就是经典的 slope trick 形式了。时间复杂度 \(O(n\log n)\)

记录

3. 染色

\(m:=m+1\),然后我们认为一行的权值是第一个白色格子的位置。

考虑 subtask4,可以通过很多手段算出最终状态每一行的美丽值。然后可以用线段树维护区间内的 min/min count,还有 sum 和 tag(表示给区间的 min position 加 tag),在 \(O(m\log n)\) 的时间内算出来。

考虑动态的情况,维护每一列上的白色连续段,每个连续段用 set 的形式存储在 \(\log n\) 个节点中(seg是从行的角度来看的),然后一个位置(行)的权值是 seg 上,根到它的所有节点的 set 里最小的列。

这样的设计是难以去维护的,因为 min/min count变的很繁琐。

但是他们只是用来决定三操作给哪些人加的,我们的 min/min count还是只对于子树信息去维护,而在三操作之前,可以在线段树上问出真正的区间最小权值,然后再下去决定给哪些人加。举例:如果根处的 set 存的就是最小权值,那么其实不是对 min pos 加而是每个位置都要加。

所以我们要维护两个 tag 了,一个是全局加的,一个是给 minpos 加的。

但是 tag 下放的时候还有学问,如果一个给 minpos 加的标记,但是 min=你当前这个节点 set 里存的 min,那么下传过后本来的 minpos 加其实变成了全局加,这个细节很有感觉。

这样时间复杂度是 \(O(m\log^2)\) 的。考虑卡常,把 set 换成可删堆。但是可删堆是不能空删的(删除一个不存在的数),所以外层维护连续段要做的比较精细。

记录

4. 遗迹

\(a_i:=2n-a_i+1\),然后我们认为每次保护的是编号比较小的。

首先第一个人一定赢,第二个人很可能赢。

第二个人赢不了肯定是 \(h_1=h_2=1\)。如果他们高度不相同那第二个人还是赢麻。

如果 \(h_1=h_2\) 那么 \(h_2\) 先掉一次然后再赢。

那这个过程拓展一下就是不断扫,当 \(h_i\) 前面出现一个高度相同的人的时候会继续减一。减成 \(0\) 就是输。

我们只关注前面出现的人的极长前缀,设 \(f(i,j)\) 是前 \(i\) 个人,\(1\sim j\) 都出现过了而 \(j+1\) 没有的方案数即可。

转移其实挺好想的,不写了。复杂度 \(O(n^3)\)

有一个细节是先默认 \(2n\) 个人本质不同这样方便算一些系数,最后除掉 \(2^n\) 即可。

记录

5. 汉堡肉

在一维的时候这是一个经典的贪心问题:每次选 \(R\) 最小的,删去被覆盖的,不断重复这个过程。

二维的时候感觉是个 NPC 啊,很有感觉。

首先 \(\min R\) 这个位置必须有一个点放这个比较显然。

由于 \(k=4\) 可以联想到 \(\max L,\min D,\max U\) 四个位置也是必须有一个点的。

\(\min R\)\(\max L\) 被一个矩形取到为例,可以发现此时退化成了一个一维的情况,因为存在一个 \(i\) 使得所有矩形都包含至少一个横坐标为 \(i\) 的点。

所以 \(\min R \lt \max L\)\(\min D\lt \max U\) 成立。

\(k\le 3\) 的时候至少有一个点是在两条边界的交界的,每次爆搜,然后对于新的若干个矩形再次这样做,大概是 \(4^k\times n\) 的。

如果四个边界有两个同属于一个矩形(只可能是邻接的边界)也可以用爆搜解决。

否则四条边界恰好各一个点被选中,考虑用 2-SAT 来构造。比较麻烦的点在于如果一个矩形交了两个边界。

首先如果一个矩形的某个边界合法(也就是答案在对应的一个区间内)相当于 \(pre_{l-1}=0\and pre_{r}=1\)

我们对上面的矩形额外开两个变量 \(x,y\) 表示两条边界的限制,\(x,y\) 分别根据上述的逻辑连到对应边界的 \(pre\)。然后 \(x\or y=1\) 保证即可。

时间复杂度 \(O(4^k\times n + n\log n)\)\(\log\) 因为要离散化。

似乎我写的还挺短的,但是跑的很慢。

记录

6. Directable as Desired

神题!感谢 zty 大人的指点orz。

把这个有标号无根树的计数变成对有标号有根树的计数,答案再除以 \(n\)

然后我们认为 \(D_i\) 条边也是带标号的,最后答案再除以 \(\prod D_i!\)

接下来考虑树的形态,有两种边:up 和 down 的。

把所有 \(u\rightarrow fa_u\)\(u\) 拿出来记作 \(S\),当 \(S\) 固定的时候我们对树的个数计数。

分为三个部分:

  • \(S\) 中的每个点,选择一条父边,这部分是 \(\prod_{i\in S} D_i\)

  • 考虑构成若干个内向树森林,根集是 \(U-S\) 的方案数。

  • 把每个内向森林视作一个点,钦定每个点的出度,拼接成外向树的方案数。(注意这部分每条边都是互不相同的,即使是一个点连出去的若干条边!)

先来考虑第二部分,根据对称性,我们只需要计算构成 \(|U-S|\) 个内向森林的方案数,除以 \(\dbinom{n}{|S|}\) 即可。

也就是相当于:每次找到一个内向森林的根 \(u\),找到一个和 \(u\) 不在一联通块的点 \(v\) 然后连接 \(u,v\)

这看起来是一个很难计数的过程,但我们考虑如果前面已经接了 \(i\) 次,不合法的 \((u,v)\) 对其实是确定的(每个连通块的 sz 之和),也就是说当前还有
\(n-i\) 个点,总数是 \(n(n-i)\) 种,去掉 \(n\) 种不合法的情形也就是 \(n(n-i-1)\),那么答案就是:

\[\prod_{i=0}^{|S|-1}n(n-i-1)=\prod_{i=1}^{|S|}n(n-i)=n^{|S|} \times \frac{(n-1)!}{(n-|S|-1)!} \]

但其实这里是会算重的,\(|S|\) 条边以任意顺序加进去都被算了一次,所以答案要除以 \(|S|!\) 也就是 \(n^{|S|}\times \dbinom{n-1}{|S|}\),再除以 \(\dbinom{n}{|S|}\) 我们得到 \(n^{|S|-1}(n-|S|)\)(验算一下,对于 \(|S|=0\) 也是成立的)。

最后一部分归结成这样一个问题:

\(n\) 个点的外向树个数,其中钦定每个点的度数。

结论是 \(\frac{(n-1)!}{\prod d_i!}\)

又因为每个点出去的边在这里互不相同,答案就变成了 \((n-1)!\)

现在放到 \(S\) 这里,也就是 \((n-|S|-1)!\)

因此对于固定的 \(S\),总共的答案为 \(\prod_{i\in S}D_i! \times n^{|S|-1} \times (n-|S|)!\).

对于总共的答案,分治 NTT 计算 \((1+D_i)\) 的每一项即可,时间复杂度 \(O(n\log^2 n)\)

记录

7. Say Goodbye

考虑树的情况,容易发现树的形态数目和染色是独立的两个问题。

而树的形态数目发现其实就是卡特兰数移动一位,也就是 \(f_n=c_{n-1}\)

考虑基环带旋转会重合这个事情,首先枚举环长(假设允许自环,最后去掉就行了),然后套 burnside。

也就是:

\[\sum_{L=1}\sum_{d\mid L}\phi(d)\times \dbinom{n/d}{a_1/d,a_2/d,...,a_k/d}\times \frac{1}{L} \times [x^{n/d}]F^{L/d}(x) \]

\(L\) 是总环长,\(d\) 是每个置换环的长度,组合数是给每个置换环都去分配颜色的方式(等价于每个环选个代表,然后把颜色数量全部除以 \(d\) 分配),而最后的多项式系数是说,一共有 \(L/d\) 个代表,他们的大小和应该是 \(n/d\)

交换求和顺序:

\[\sum_{d}\phi(d)\times \dbinom{n/d}{a_1/d,a_2/d,...,a_k/d}\times \sum_{d\mid L}\frac{1}{L}\times [x^{n/d}]F^{L/d}(x) \]

考虑后面的这个东西:

\[\sum_{d\mid L}\frac{1}{L}\times [x^{n/d}]F^{L/d}(x) \\ =\sum_{i=1}^{n/d}\frac{1}{id}\times [x^{n/d}]F^{i}(x)\\ =\frac{1}{d}[x^{n/d}]\sum_{i=1}^{n/d}\frac{F^i(x)}{i} \]

看到 \(\sum \frac{F^i(x)}{i}\) 很容易联想到 \(\ln(1+x)=\sum_{i=1}^{\infty}(-1)^{i+1}\frac{x^i}{i}\)。事实上,我们要求的结果,和次数大于 \(n/d\) 的项完全没有关系,所以可以认为求的就是 \([x^{n/d}] -\ln(1-F(x) )\)

时间复杂度 \(O(n\log n + d(g)\times k)\),其中 \(g=\gcd\{a_1,a_2,...,a_k\}\)

记录

8. 简单算术

相当于我们要确定系数组 \((c_0,c_1,...,c_n)\),满足 \(\sum c_i=m\)\(\sum c_i\times i=k\),且贡献是 \(\dbinom{m}{c_1,c_2,...,c_m}\times \prod a_i^{c_i}\)

这个组合数对小质数 \(p\) 取模应该已经是典中典了,相当于对于 \(p\) 进制下的每个数位,要满足 \(\sum c = d\)

和 ARC156D 一模一样的数位 dp 方式(注意到 \(a_i^{pk}=a_i^k\)),我们设 \(dp(i,j)\) 是确定了低的 \(i\) 位,在 \(k\) 这个角度来说进位是 \(j\),所有局面的贡献和。

为了转移,我们需要求出 \(g(i,j)\) 表示 \([x^j]F^i(x)\),其中 \(i\lt p,j\le np\)

状态有 \(O(n\log K)\) 个,每次如果暴力转移是 \(O(pn)\) 的,但是注意到由于 \(k\) 在数位上的限制,每个状态的合法转移模 \(p\) 是同余的,因此可以做到 \(O(n^2\log K)\) 单次。

佳老师加强了一下(对于 \(p=2,3\) 可以令 \(p=256,243\) 来平衡复杂度),太厉害了。

记录

9. 矩形区域

好像没有非常困难,但感觉也没有非常简单...

我们来考虑一些暴力点的想法,比如说枚举左上角 \((x,y)\)

我们可以预处理一些东西:\(f(i,x,y)\) 表示第 \(i\) 列的 \(x\sim y\) 这一段是否合法,\(g(i,x,y)\) 表示第 \(i\) 行的。

非常显然一个矩形合法就相当于列的每一段和行的每一段都合法。

而且我们可以更进一步算出 \(f'(i,x,y)\) 表示最长的 \(j\) 满足第 \(i,i+1,...,i+j-1\) 列的 \((x,y)\) 都是合法地,类似定义 \(g'(i,x,y)\)

这些东西都是不难在三方的时间内预处理出来的(虽然最后我们是要一个平方级别的算法)。

在枚举左上角 \((x,y)\) 之后,我们枚举矩形的最底端所在行 \(r\ge x\),令 \(c=f'(y,x,r)\),也就是说如果矩形的最底端是 \(r\) 则最多能向右延申 \(c\) 格,把它看作一个点 \((r,c)\)

然后我们来重新枚举矩形最右端所在列 \(c\ge y\),类似地令 \(r=g'(x,y,c)\),也就是如果最底端是 \(c\) 的话最多能向下取到 \(r\)。把它也看做点 \((r,c)\)

我们把第二类点视作询问,则实际上就是说去数一个询问 \((r,c)\) 内有多少个一类点。

这是一个经典的二维偏序,排序后 BIT 即可。

这样我们得到了一个 \(O(n^3\log)\) 的做法(不妨令 \(n,m\) 同阶),拼凑一下几个子任务恰好可以获得 50pts 的好成绩。

进一步的优化实际上比前面这部分简单很多:考察一维的情况,若 \([l,r]\) 合法,不妨令 \(a_{l-1} \le a_{r+1}\) 则一定有 \(r+1\) 是第一个在 \(l-1\) 右边且大于等于 \(a_{l-1}\) 的位置。

根据这样的形式容易发现其实 \(f,g\) 不为 \(0\) 的值只有 \(O(nm)\) 个且容易用单调栈预处理,然后预处理就变成平方了,考虑数点的过程,以求第一类点位例,如果 \(f'(y,x,r)\) 不为 \(0\) 那么它一定对应一个挂在 \((x,y)\) 处的竖条(列上的合法区间),而条的总个数也只有 \(O(nm)\) 个,因此我们从枚举所有行,改为暴力枚举所有竖条;对于第二类同理。

这样就在 \(O(n^2 \log)\) 的时间内解决了。

记录

10. Osady i warownie 2

神题。

我们考虑维护能向右就向右和能向下就向下的两条合法路径(记作 \(R,D\)),然后会发现很多事情都很简单了;唯一的问题在于如果一条路径被一个障碍放上了,要重新更新该条路径,这个看上去是一个很不可做的东西。

冷静一下会发现(以 \(D\) 的更新为例),设前后路径为 \(D,D'\),则夹在他们中间的障碍(也只有这些障碍)才会影响 \(D'\) 的形成且在此之后他们都等于没用了。

因此我们如果能做一个 \(O(c)\) 级别(\(c\) 是影响决策,或者说堵了路的障碍物个数)的东西来寻找新路径复杂度就对了,而且这个看上去就比刚才可做多了。

这个只是大体思路。在维护上也是非常神仙的。

我们用一个长度为 \(n\) 的数组来描述 \(D\) 这条轮廓线:\(r_1,r_2,...,r_n\)\(r_i\) 表示在路径上,第 \(i\) 行最右边会走到哪一列。

首先 \(r_i\) 是一个保持单调不下降的数组,考虑在这条线的 \((x,y)\) 上放一个障碍。

如果没有其它障碍了,则相当于从 \(x-1\) 行开始,到 \(n\) 位置,做 \(r_i:=\max\{r_i,y+1\}\)(在保证尽量向下的同时绕过 \((x,y)\))。

问题是可能这条路还会有新的障碍,首先考虑路径的变化:从之前的一个位置向右走一些步,然后再向下走一些步数(而且是严格向右,向下的)。

那么向下的话只关注碰到的第一个新障碍,向右的话关注的是在转折点之前碰到的最后一个,然后我们先调整向下的这块,再调整向上的这块就可以了(因为有贪心决策在所以肯定是优先从小的地方开始调整)。

这里对 \(r\) 的修改直接上 BIT(后缀取 max 单点求值等价于单点修改,求前缀 max;拜谢三维),时间复杂度 \(O((n+m+k)\log )\)

LOJ 上随便过,洛谷的话还是要卡卡的。

记录

11. 密码锁

仅考虑 \(k=4\)

不妨认为 \(\max\) 始终位于第一行,则 \(\min\) 不能和它同行(不然答案就顶到 \(\max-\min\))了,枚举一行放 \(\min\)

二分答案,那么这两行的范围区间就确定了。对每个转轮的 \(4\) 种转法,有若干种是满足这两行的要求的,考虑拿出来,剩下的两行放到平面上构成 \((x,y)\)

现在问题变成了:平面上每种颜色的点若干个,是否存在一个 \(L\times L\) 的矩形,使得其中每个颜色的点都有出现。

\(x\) 这一维度扫描线,对于一种颜色 \(i\) 假设剩下了若干个,则每个点的 \(y\) 坐标限制了矩形 \(y\) 轴上的取值:限制是一段区间,至少要落在其中一个。

暴力把有交的合并起来,现在限制是若干个不交矩形。

问题变成了区间加,全局求 max,线段树维护即可。

时间复杂度 \(O(nk^3\log^2)\)

记录

12. 小 Y 和二叉树

其实感觉,还是比蓝题难的。

考虑枚举根以后 \(O(n)\) 做这个问题,设 \(f(u)\) 表示 \(u\) 子树内最小的叶子即可,这样 \(O(n^2)\) 可以获得 40pts 的好成绩。

链的情况下我们不妨让左端点的编号比右端点小,则找到第一个编号比右端点大的中间点,在它之前的一个点作为根,这样相当于把右端点插入到了它之前。

对于度数 \(1/3\) 的情况,也就是我们只能选择叶子当根,且定根后,每个点的度数(不算父亲)是 \(0/2\)

因此有性质,在我们对一棵树 dfs 的时候,第一个点一定是某个叶子(这在度数可能为 \(2\) 的时候是不一定成立的)。

从编号最小的叶子 \(p\) 开始:如果当前节点没有分叉了,则根就必须是它;否则它一定有两个分叉还没有考虑,设为 \(a,b\)

如果根在 \(a\) 里(显然根不在 \(a\) 就在 \(b\),不可能在当前节点里),那么下一个点一定是 \(b\) 的某个儿子(实际上下一个点是当前点之后的下一个点),反之是 \(a\) 的某个儿子。

随便定个根,则两个分叉所在的连通块要么是某个子树,要么是某个子树的补集,容易 \(O(n)\) 预处理出来每种情况下最小的叶子编号。

然后就能确定根了。和链还有 \(n^2\) 拼起来可以获得 70pts 的好成绩。

考虑冲一手,来拓展一下,其实度数为 \(2\) 的点和叶子一样都是可以成为某个连通块里第一个加入 dfs 序的点的,那就做完了。

还有一个问题是,此时可能会出现当前点还剩一个分叉的情况,这个时候如果选当前点是可能的,这样下一个点(同样也是加入当前点后)肯定就是那个分支里和当前点挨着的那个。

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

记录

13. Banany

又到了喜闻乐见的卡常小技巧。

非动态开点做到两倍空间的线段树:idx(L,R)=(L+R) | (L!=R);

好的其实就是看到树上路径 + 动态修改询问,想到上点分树,那么修改 \(z\) 还是很好维护的,修改边看着有点麻烦。

其实不难做啊,假设修改的是 \((x,y)\) 这条边(\(fa_y=x\)),那体现在点分树上有两种:

由于我们对点分树的每个点 \(u\) 和点分树内(不是原树)\(u\) 子树的每个点 \(v\) 维护的都是 \(dis(v,u)\),如果修改一条边而 \(dis(v,u)\) 被修改了,那么在原树上 \(u,v\) 肯定一个点是在 \(y\) 里面的另一个点不在。

如果 \(v\)\(y\) 子树里而 \(x\) 不在,那么这个点设为 \(p\),注意到 \(v\rightarrow p\) 的路径经过 \(x\) 所以 \(p\) 一定也要是点分树上 \(x\) 的祖先才行。

所以暴力枚举 \(x\) 的祖先判其是否真的不在 \(y\) 里即可,然后体现在对这个祖先的修改,就是对它所附属的信息里 dfn 连续的一段(原树 dfn)做修改,容易想到排序后建 seg 做做。

另外一类,\(v\) 不在而 \(x\) 在同理,此时路径一定经过 \(y\),所以我们枚举 \(y\) 的祖先并且看它是否在 \(y\) 子树内即可。

对于 \(t\neq s\) 这个事情,有两种解决方案:

  1. 维护 mx 和 mx'(空间过不去)。

  2. 询问前令 \(z_s=-\infty\) 询问后撤销即可。

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

谢谢 crz 大人搬题的时候把空间开到了 512M,磕头了。

记录

14. Process with Constant Sum

考虑全局问一次怎么做。

首先不难想到把用 \(1\) 操作让每个位置变成 \(0/1\) 序列(除了最后一个)。

发现 \([l,r)\)\(r\) 是不一样的,我们先来考虑 \([l,r)\)

目标是让剩下的 \(1\) 尽可能多。

考虑一次二操作如果两边都是 \(1\) 那么就变成了 \(0\),否则就相当于把 \(1\) 移动了两位。

显然每个 \(1\) 位置的奇偶性是不变的。

如果两个 \(1\) 中间只有奇数个 \(0\) 那么一定能消它们,贪心地能消就消一定是最优秀的。

否则任意两个 \(1\) 中间都是偶数个 \(0\),这是最后的局面。

则它们所处位置的奇偶性是不一样的。因此不是 101010... 就是 010101...

考虑线段树维护合并信息:记录还剩的个数以及开头的是 0 还是 1 就可以 $O(1) 合并了(还要记录一下 \(1\) 的总个数)。

考虑 \(r\)\(0\) 有几个比较显然的必要条件:最开始 \(\max \lt 2\)\(a_r=0\)

然后如果产生了合并则一定有一个人的真实值变成了 \(2\),这个时候它也不行。

注意!还有一种非常小的特判:考虑 \([l,r)\) 这里贪心消完以后剩下的最靠后的一个 \(1\),位于 \(x\),显然 \((x,r)\) 全是 \(0\),如果这个个数是奇数且 \(a_r\) 最后不为 \(0\)(即使看上去 \(b_r=0\) 但是如果 \(\max\ge 2\) 或者产生了合并它其实还是废物),我们可以让 \(r\) 更加废物换取 \(a_x=0\)

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

15. 基础图论练习题

考虑竞赛图求 scc 个数有兰道定理在:把出度 \(d_i\) 排序后前缀和 \(s_i=\dbinom{i}{2}\) 的个数即为 scc 个数。

反转 \(u\rightarrow v\) 相当于 \(d_u\) 减一,\(d_v\) 加一。

两个位置的变化是困难的,考虑枚举 \(u\),先把 \(d_u\) 减一然后排序。此时变成了选择一个 \(v\),把 \(d_v\) 加一。

注意此时可以认为相对顺序没有发生任何变化,设 \(ed_x\)\(d \le x\) 的个数,可以认为 \(v\) 的排名就是 \(ed_{d_v}\) 且加一后排名显然也可以是 \(ed_{d_v}\)

因此预处理 \(pre_i\) 表示前缀 \(s_i=\dbinom{i}{2}\) 的个数以及 \(suf_i\) 表示后缀 \(s_i=\dbinom{i}{2}-1\) 的个数即可,复杂度 \(O(n^2\log n)\),把排序换成基数排序就可以 \(O(n^2)\)

记录

16. All Pair Shortest Paths

分治做这个问题,考虑分成 \([L,mid]\) 以及 \((mid,R]\) 两部分,对于 \((0,mid)\)\((1,mid)\) 求出左边每个点分别到它们的最短路;类似地,右边对 \((0,mid+1)\)\((1,mid+1)\) 求。

考虑所有路径跨越左右的点对,一定是左边选一个点 \(x\) ,右边选一个点 \(y\) 得到的。

设每个点走到上面那个分界线的答案是 \(f\),下面的是 \(g\)

则相当于求 \(\sum_{x,y} \min\{f(x)+f(y),g(x)+g(y)\}\)

枚举 \(x\),发现 \(f(x)+f(y)\le g(x)+g(y)\) 等价于 \(f(x)-g(x) \le g(y)-f(y)\),那么把右边的按照 \(g(y)-f(y)\) 排序后二分就能算答案;\(f(x)+f(y)\gt g(x)+g(y)\) 同理。最后对 \(y\) 也做一遍上述过程即可。

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

记录

17. DAG Generation

我觉得我能做出这个题已经算脑子很在线了/yun

总方案数是 \(n! \times 2^{\dbinom{n}{2}}\) 的平方。

然后变成计数问题,不妨考虑两张图是一样的方案数。

\(f(G)\)\(G\) 的拓扑序个数,则求:

\[\sum f^2(G) \]

等价于随便枚举两个拓扑序 \(A,B\)。 设 \(g(A,B)\) 表示同时两个拓扑序的 DAG 个数:

\[\sum g(A,B) \]

不妨令 \(A\)\(\{1,2,...,n\}\)

则:

\[n!\sum g(B) \]

\(g(B)\) 即为 \(2^{cnt}\)\(cnt\) 即顺序对个数。

考虑排列和顺序对数组一一对应:任何一个长度为 \(n\) 的数组 \(a\),满足 \(a_i\lt i\) 都和一个排列一一对应,且顺序对数目就是 \(\sum a_i\)

也就是 \(\sum_a 2^{a_1+a_2+...+a_n}\)

对每个位置 \(i\)\((2^0+2^1+...2^{i-1})=2^{i}-1\),前 \(n\) 项乘起来即可。

18. 数列重排

\(\lt k\) 的数是比较特殊的,而 \(\ge k\) 的数是有区别的。

我们考虑这个题有一个显然的上界:任何一个 \(\ge m\) 的区间都合法。

考虑这是取不到的,只要有一个 \(\ge k\) 的数就会寄。

而当 \(k=m\) 的时候它其实是可以取到的:构造 \(x\) 个排列,每个排列都是把出现次数 \(x+1\) 的那些放前面,剩下的放后面,把这个排列复制 \(x\) 边,最后剩下的多出一次的数按照这个顺序放在最后就好了。

考虑一般的情况,对于 \(\lt k\) 的而言,这个结构是不会变的,也就是我们只会把 \(\ge k\) 的数插空进去。

如果没有末尾的那个残余排列会好看很多,但事实上残余排列和最后一个完整排列直接是可以合并的(也就是不会插数进去),如果有,你可以把它们放到最右边。

考虑不合法的数量,首先以两个 \(\lt k\) 的数作为端点的不合法区间,此时是容易算的。我们考虑如果在中间插入 \(x\) 个产生 \(C(x+1,2) + (2k-2)x\) 个,在两边插入的话产生 \(C(x+1,2) + (k-1)x\) 个。

两个都是二次函数,二次函数是凸函数,根据典中典贪心我们每次选斜率变化最小的拓展 \(1\) 即可。

因此可以发现前 \(2(k-2)\) 次我们会选两边的,各 \(k-1\) 个,然后它们的斜率变化量此时开始没有任何区别,也就是全部地位相等,平均分配即可。

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

记录

19. Fuel

首先令 \(a_i,b_i\) 都除以 \(c_i\),这样相当于我们在原限制不变的情况下最大化 \(\sum x_i\) 即可。

如果只能选择一种油则就是 \(\min\{A/a_i,B/b_i\}\)

考虑多种油可以混合比较麻烦:可以认为是先混合出某种混合油,设 \(1\) 单位的混合油答案是 \(a',b'\),然后最大化 \(\min\{A/a',B/b'\}\)

考虑只有两种油混合,也就是研究向量方程 \(v1x+v2(1-x)=b\) 对于哪些 \(b\) 有解。

显然在 \(v1\)\(v2\) 的连线段上的每一个点都是能表示出来的。

再加入一种油同理,该线段每个点和 \(v3\) 的连线段,合在一起就是一个三角形。

对于若干个点,根据上述过程,显然我们能表示出的油就是 \(n\) 个点的凸包。

现在问题变成了在凸包内选一点最大化 \(\min\{A/x,B/y\}\)

首先不难看出只有边界的点有意义。

考察边界的一个点,如果它不在顶点上且不满足 \(A/x=B/y\),则要么 \(x\) 减小一点,要么变大一点,总有一种方式让答案不会更劣。

因此我们只关注 \(A/x=B/y\) 或者在凸包顶点上的点。

后一种是容易的,前一种也就是适合方程 \(y=\frac{B}{A}x\),求凸包和这条直线的交点即可。

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

记录

20. Random Radix Sort

第一次自己独立想出 ARC F 啊,很有感觉。

考虑倒着做这个问题。

最后一次按照第 \(k\) 位排序后,显然在 \(b\) 中的答案按照这一位是升序排列的。

若两个元素的第 \(k\) 位不同,在更往前的排序中我们不再关注其相对顺序,我们只需要让所有 \(b\) 相同的人在 \(m-1\) 次排序后相对顺序正确即可。

推广到还剩前若干次操作没有研究的时刻:也就是序列 \(b\) 被分成了若干段(最开始的时候只有一段),此时如果按照 \(k\) 这一位排序,则首先要求每一段内按照 \(k\) 是升序的,然后,若 \(b_i\)\(b_{i+1}\) 的第 \(k\) 位产生不同,则会在这里劈开分成两段。

根据这样的描述我们会发现,只关注每种操作是否在后面使用了,不过加入这些操作的顺序可能有很多种。

\(dp(S)\) 表示操作集合是 \(S\) 的方案数,考虑如何判断能否转移。

首先,相当于每个 \(0\le i\lt K\) 都有一个数组 \(c_i\),表示那些 \(b_i(k) \gt b_{i+1}(k)\) 的位置;另外还有一个 \(d_i\),表示那些 \(b_i(k) \neq b_{i+1}(k)\) 的位置。

若集合 \(S\) 能加入 \(i\),当且仅当 \(c_i\) 是集合 \(S\) 的每一个元素 \(j\)\(d_j\) 并起来的结果的子集。

朴素做就可以有 \(k\times 2^k\times n\) 这个级别的做法,我们考虑加速,bitset 是一个方向,但算量依旧很大。

首先枚举 \(i\),然后考虑对于 \(c_i\) 的每个位置 \(j\),我们可以统计出哪些位置的 \(d\) 是包含 \(j\) 的,记作 \(mask\)

则相当于给出了 \(|c_i|\) 个约束,每个约束要求 \(S\)\(mask_i\) 的交集非空。转化一下,不合法等价于存在一个 \(mask_i\)\(S\) 的补集的子集。

因此我们初始令 \(f(mask_i)++\),然后对 \(f\) 做高维前缀和,如果 \(f(U - S)\) 不为 \(0\) 那么 \(S\) 就是非法的(\(U\)\(\{0,1,...,k-1\}\))。

总而言之可以在 \(O(k^2\times 2^k)\) 的时间内完成转移,考虑统计答案。

由于一个数可能出现多次,看上去不太好确定 \(a,b\) 内元素的对应关系,但实际上注意到相同的数,相对顺序不会变化(永远是稳定排序),因此分别对 \(a,b\) 的元素替换成二元组 \((val,cnt)\)\(val\) 是原始值,\(cnt\) 是之前有多少个 \(=val\) 的数。注意到这样的话就可以认为 \((a,b)\) 是排列了。

然后我们考虑哪些分段方案最后是合法的,当且仅当对于 \(b\) 的每一段:里面的数在 \(a\) 中也是按照这个顺序出现的。

因此考虑 \(b\) 中的相邻两个数 \(x,y\),如果在 \(a\)\(y\)\(x\) 出现地更早则此处必须断开。因此相当于钦定了一个必须被断开的位置集合,注意到此时这个问题和转移时毫无二致,直接做一次 \(k\times 2^k\) 的高维前缀和就能解决了。

统计答案的时候实质上要算一个,长度为 \(m\) 的数组恰好出现了 \(k\) 种颜色,且首次出现的顺序必定是 \(1,2,...,k\) 的方案数。首先后面的内容可以忽略,算出来后除以 \(k!\) 即可,前面的“恰好”容斥掉就好了,这里大概是 \(k^2\) 级别的。

记录

21. Minimum Cost Paths

\(dp(x,y)\) 表示走到 \((x,y)\) 的代价,猜测 \(dp_y\) 关于 \(x\) 下凸,根据套路只要归纳地做就好了。

首先 \(dp(x) += x^2\),原函数加凸函数依旧是下凸的。

然后考虑要做的是 \(f(x)=\min y\le x dp(y) + k\times (x-y)\)

本质上相当于 \(-dp\) 的前缀 \(\min\) 函数和 \(y=kx\) 加起来,由于下凸函数的前缀 \(\min\) 是凸的因此结果还是凸的。

如何实现,其实相当于是从原函数的每个位置开始引一条斜率为 \(k\) 的射线去更新,只需要找到整个凸包第一处斜率 \(\gt k\) 的转折点,用从它开始的一次函数来替代后面所有的函数取值即可。

可以发现,任何时刻这个凸壳可以被描述成若干分段函数,每一段都是一个二次函数,用 vector 维护一个栈结构的连续段序列,从末尾开始不断检查并修改,即可做到 \(O(n\log n)\)

记录

22. MUL-Multidrink

首先考虑把 \(1\rightarrow n\) 的链拉出来,显然我们是按顺序遍历的。

对每个点挂着的子树分开来 dp。

考虑几种情况:

  • \(u\) 进去 \(u\) 出来。

  • \(u\) 进去 \(u\) 的儿子出来。

  • \(u\) 的儿子进去 \(u\) 出来。

  • \(u\) 的儿子进去,\(u\) 的儿子出来。

二三两种情况其实是一样的,任何一种二方式倒过来就是三的方式,所以其实可以合并在一起。

\(f(u,0/1/2)\) 来表示就行了。

\(f(u,0)\) 就是等价于 \(u\) 是否为叶子。

\(f(u,1)\) 是说,第一步你可以选择是走一步(\(f(v,0)\) )还是两步 (\(f(v,1\)),因为不管怎么说都只能从 \(v\) 而不是 \(v\) 的儿子出来,后续的都是 \(f(u,0)\)

\(f(u,1)\) 是说,第一次选一个儿子 \(v\),第一次可以从 \(v\) 的儿子出来(当然也可以从 \(v\) 出来),还有一次机会可以从 \(u\) 开始,选择一个 \(v\) 的儿子,然后从 \(v\) 出来。

也就是至多两个 \(f(v,0)=0\) 的,然后剩下两个都是 \(f(v,1)=1\) 的。

这样就能做了,最后在链上再设 \(g(u,0/1)\) 表示走完了 \(u\) 子树位于 \(u\) / \(u\) 的儿子就好了。

方案就按照 dp 的过程做,时间复杂度 \(O(n)\)

记录

22. K-Coloring

不妨认为图是联通的。

考虑 \(3^n\) 可以直接用容斥做状压 dp。

对于 \(n\) 比较大的情况,注意到 dfs 树的返祖边较少,\(Bell(m-n+1)\) 枚举返祖边顶上的点的颜色集合关系,然后相当于是确定了所有返祖边顶点的颜色,做 dp。

\(f(u,k)\) 表示 \(u\) 的父亲颜色是 \(k\) 的方案数,注意到 \(k\) 实际上只有 \(O(n)\) 个特殊取值,再加一个状态表示一般的颜色即可。

这样可以 \(O(n^2)\) 做出 dp。

所以是 \(O(\min\{3^n,Bell(m-n+1)\times n^2\})\) 的,考虑按照 \(n\le 19\) 分治比较平均可以过。

记录

23. Sequence of Substrings

考虑 dp:设 \(f(i,j)\) 表示最后一个用的是 \([i-j+1,i]\) 的最长长度。

考虑转移:枚举下一个串和当前的 lcp ,然后加一个合法字符进去。

所以每次长度至多增加 \(1\),所以串长 \(O(\sqrt n)\)

这样 dp 状态就减少了,但从 dp 角度还是 \(O(n^2)\) 级别的。

不妨直接把所有串事先按照字典序排序,然后变成了找最长的连续不交子序列(且出现位置上升)。

这就可以直接 bit 了。

考虑排序可以建出 01 trie 然后前序遍历即可。

时间复杂度 \(O(n\sqrt n\log n)\),常数很小。

记录

24. Ember and Storm's Tree Game

你最好省选也能场切 *3400.jpg 😅

如果给一个数组,先手怎么操作让它合法?

注意到,如果选择了 \(i\),那么 \([1,i]\) 必须是单调的,且 \((i,n]\) 也必须是单调的。

那么有四种情况,以 \([1,i]\) 升序为例子,有两种:\((i,n]\) 升序或者 \((i,n]\) 降序,(我们假设原序列不是升序的)。

我们会发现第一种情况不合法,第二种是可以操作的。

因此我们导出结论:单峰单谷(非退化)的序列有操作方式。

以单峰为例,考虑峰顶 \(i\),把它划分给左边或者右边都有恰好一种方式,因此方案数是 \(2\)

现在考虑初始就是排好序的样子,我们发现令 \(i=1\) 或者 \(n\) 也恰好有一种方式(因为一个孤立点可以认为是升或者降的),所以它也是两种。

因此单峰单谷(允许退化)都有两种合法操作方式,且没有其它的序列可以恰好一次操作变得合法。

现在考虑序列的情况,也就是 \(n\ge 3,d=2\),先手会创建一条链,使得任意一个子区间不是单峰就是单谷的,非常显然,这意味着整个链就是单谷的。

那么合法的链数 \(\times n\times (n-1)\times 2\) 也就是此时的答案,至此可以在模拟赛获得 \(15pts\) 的好成绩。

考虑一般树的情况,也就是我们要对树计数:满足任意一条路径的点写出来,都是单峰/单谷的。

我们以 \(1\) 定根,考虑每条边,如果 \(u\gt fa_u\) 则认为其是白色的,否则认为其是黑色的。

显然,至少有一条白色边,且白色边必须是以 \(1\) 为根的一个连通块。

我们考察此时一条路径是否合法:把 \(x\rightarrow y\) 的路径写出来,考虑每条边,如果是向上走的,则写下这条边的颜色,否则写下其反色。

则如果是一条合法的路径,写下来必定是 BBBB...WWWW 或者 WWWW...BBBB 的。

如果这棵树全是白色边,非常显然它是合法的,这样,方案数是 \((n-1)!\)

否则我们假设其有至少一条黑色边,注意到你向上爬,最后爬到 \(1\) 的那次,一定是白色边。所以任何一条黑边向上爬都会爬到那个白色边构成的连通块,此时我们如果继续爬,这样的路径是合法的,如果我们爬到某个点转而向下,这些点必须全是黑色的(因为此时取其反色)。

因此我们导出一个很重要的结论:一定存在一个点 \(u\),使得 \(1\rightarrow u\) 的路径全是白色边,我们称这条根链为主链,在末端(\(u\))挂了一颗全是白色边的子树,然后是若干颗黑色边构成的子树挂在主链上,这是一棵树合法的充要条件。

一个问题是,我们发现这样的 \(u\) 在一棵树有多个,事实上,设主链上深度最深的,被一个黑色子树挂着的点是 \(p\),则主链上 \(p\) 及其之后的点都可以作为 \(u\)

所以完全可以枚举 \(u\),然后它要满足两个条件:

  • 子树内全是白色边。

  • 至少有一个黑色子树挂在 \(u\) 上。

其实也就是让深度最浅的作为代表。

第二个条件,比较容易,相当于所有 \(\gt u\) 的点都在这个子树内了(也仅有这些点),随便 dp 一下:设 \(dp(i,j)\) 表示编号 \(\ge i\) 个点确定了还剩 \(j\) 个父亲没确定的方案数;对于前面也是类似的,不过编号从小到大,且你可以决定每个点是否需要确定父亲(如果让他在主链上,则不需要再安排父亲了)。

这样的话就可以 \(O(n^3)\) 做任意树了,其实带度数限制没有什么区别的,只不过每个点接纳的儿子数量有一个限制罢了,这个要注意一下,第一部分应该都是 \(d-1\),但是第二部分如果是主链上的点那最多只能容纳 \(d-2\) 颗黑色子树。

时间复杂度 \(O(n^3)\)

还是那句话,希望我省选也能场切这种题😅

记录

25. Count Unlabeled Graphs

首先这个问题可以通过容斥把恰好去掉。

然后考虑这个问题强于无标号无向图计数,先考虑这个问题怎么做,这个数据范围如此小感觉就是挑战图灵奖什么的。

考虑套 burnside 引理:枚举排列 \(p\),然后也就是如果 \((i,j)\) 有边则 \((p_i,p_j)\) 也有边,这个可以并查集 \(n^2\) 求。

带颜色的话那么每个置换环都有 \(k\) 种选择,这样一个排列可以 \(n^2\) 求答案。

这样就有 \(O(n! \times n^2)\) 的做法了。

但我们其实只关注置换环的形态,也就是可以 \(p(n)\)(拆分数) 去枚举这个东西(约 \(5000\)),然后我们就按顺序给每个点标号做一下,最后求有多少个可能的排列置换环长这样,总而言之是容易 \(O(p(n)n^2)\) 的。

记录

26. E or m

显然,不是所有的局面都能被生成,我们研究一下一个局面怎样才能被生成。

如果 \((i,j)\)\(1\),则要么第 \(i\) 行的前 \(j\) 个都是 \(1\),要么第 \(j\) 列的前 \(i\) 个都是 \(1\)。每个格子都合法是局面能被生成的充要条件。

这样我们就可以做了:考虑 \(dp(i,S)\) 表示前 \(i\) 行考虑完后,\(S\) 集合所在的列,满足前 \(i\) 行里都为 \(1\)

转移的时候很显然是 \(dp(i,S) + T \rightarrow dp(i+1,S\cap T)\)

这里有两个约束:

  • 首先 \(T\) 要满足给出的矩阵,在第 \(i+1\) 行给它的约束,枚举 \(T\) 后容易 \(O(1)\) 判断。

  • 其次设 \(T\) 的极长 \(1\) 前缀是 \(i\),则 \(i\) 之后的位置是 \(S\)\(i\) 之后的位置的子集。

不妨枚举 \(i\)\(S\) 然后做 sos dp:这里我们前 \(i\) 位是不动的。

最后枚举 \(i\)\(S\cap T\),就可以还原出真正的 \(T\)

瓶颈在于每次要做 \(m\) 轮 sos dp,那么时间复杂度是 \(O(nm^2\times 2^m)\) 的。

记录

27. Souvenirs

考虑设答案点对是 \((x,y) (x\lt y)\)

不妨令 \(a_x \gt a_y\),那么枚举 \(x\),找到第一个后面比它小的位置作为第一处 \(y\)

假设 \(y\) 之后还有一处位置 \(a_z\lt a_x\),则 \(a_z\gt a_y\) 必须满足。

\(a_x - a_z \ge a_z-a_y\)\((x,z)\) 没有 \((y,z)\) 优秀,否则满足 \(a_z\ge \left\lceil \frac{n}{2} \right \rceil\)

因此有意义的点对是 \(O(n\log V)\) 级别的,找出来以后就是简单二维数点,直接 BIT。

找点对的过程可以将 \(a\) 排序后按顺序激活 + seg 二分来实现,这部分是 \(O(n\log V\log n)\) 的。

而最后的部分是 \(O((n\log V+q)\log n)\) 的,所以开大 \(q\) 就可以卡掉莫队了。

记录

28. Sonya Partymaker

考虑二分答案,然后变成了每个人可以选择 \([a_i-mid,a_i]\) 或者 \([a_i,a_i+mid]\) 两个区间覆盖。

如果是链,我们可以直接设 \(dp(i)\) 表示 \(a\) 最小的前 \(i\) 个人确定了,能覆盖的极长前缀长度。

转移:如果 \(dp(i-1) \ge pos_i-1\),则 \(dp(i)=R_i\)

否则,我们就要选择 \([L_i,i]\) 而不是 \([i,R_i]\)

根据本题目中 \(dp\)\(R\) 的单调性,如果 \(dp(i-2) \ge L_i-1\) 则我们可以选择 \(R_{i-1}\)\(pos_i\) 的较大值取答案;否则如果 \(dp(i-1) \ge L_i-1\) 我们的答案就是 \(pos_i\),否则说明不能拓展极长前缀,\(dp(i)=dp(i-1)\)

总而言之是容易 \(O(n)\) 的。

考虑环上解决这个问题,如何断环成链?

随便枚举一个人,然后枚举他的两种方向。设他此时覆盖了区间 \([L,R]\),如果一个点不在 \([L,R]\) 中,我们容易将它直接纳入断环成链的考虑内,但是如果另一个人在其中,则比较复杂。

注意到这些人里如果人数 \(=0\) 则无需考虑,如果人数 \(\ge 2\),则一定是最左的人向左,最右的人向右;如果人数 \(=1\) 则需要讨论。

此时 \([L,R]\) 其实是会扩大的,但是新拓展的区间覆盖的人,它一定只有一个方向是比较优秀的(比如说如果你把 \(R\) 增大,则新覆盖的这些人他们的左端点都不可能比 \(L\) 小),这样就成功断环了,时间复杂度 \(O(n\log m)\)

细节还是比较多的。

记录

29. Tree Depth

考虑笛卡尔树的深度有更良好的意义:\(dep_u\) 是所有合法的 \(v\) 的数量,满足:\(v\)\(a_{u\sim v}\) 的最小值(不妨假设 \(v\gt u\),另一种情况是类似的)。

考虑和逆序对结合起来,排列的逆序对有逆序数组这个一一对应的东西在,但这样的话无法和 \(a_v\) 是最小值很好地结合起来。

事实上逆序数组的本质是一个长度为 \(n\) 的排列往左/右加数,则逆序对的变化可以取 \([0,n]\) 中的任意数值,且只要生成排列的方式不变,得到的逆序数组和排列一一对应。

我们考虑换个方式来生成排列:从 \(u\) 开始加加到 \(v\),然后把剩下的加了。

这样 \(v\) 是最小值等价于加入的第 \(v-u+1\) 个数刚好取贡献 \(v-u\)

这样我们就可以把前 \(v-u\) 个数和后面 \(n-(v-u+1)\) 个数的 dp 拼起来,这样是 \(n^2k\) 的,注意到拼接的时候只关注 \(v-u\),所以枚举它,提前计算好答案,贡献给每个 \(ans_u\) 即可,时间复杂度 \(O(nk)\)

预处理的时候数组可能会开不下(我开了两个三方数组 110M 左右),前缀的 dp 值可以滚掉,后缀的 dp 值看上去滚不掉,但我们可以用带撤销的背包去做,这样空间可以做到 \(O(n+k)\)

记录

30. 背包

这道题就当作是同余最短路的复习了。

这个题和 ARC096F 非常像,对于数据范围很大的背包,我们先来考虑贪心:也就是找到单位价值最高的那个物品 \(x\)

由于本题要求恰好为 \(V\),所以其它物品的体积和在模 \(v_x\) 意义下是和 \(V \mod v_x\) 相同的。

这样我们就可以建出同余最短路的模型了,因为我们知道:对于剩下 \(n-1\) 个物品的方案 \((v,c)\),我们首先要让 \(\left \lfloor \frac{v}{v_x} \right\rfloor\) 最小,在此基础上让 \(c\) 最大。

这样我们可以直接 spfa,用一个双关键字的 pair 去维护就好了。

Alex_wei 指出同余最短路有更简洁的写法:对于每个物品在模 \(v_x\) 意义下构成的每个环,转移是独立的;对于每个环,我们绕两圈,每次都从 \(f_u\) 更新到 \(f_{nxt_u}\),这样可以考虑到所有的转移。

在实现上,注意到,其等价于最小化 \(c-\left\lfloor \frac{v}{v_x} \right\rfloor\times c_x\),所以也不需要用 pair 记录信息。

时间复杂度 \(O(nv + q)\)

记录

31. Middle Point Graph

考虑我们随机生成 \(n\) 个点,认为四点共面的概率是为 \(0\) 的,所以这个期望在诈骗,大概就是说算上中点的话不管你怎么生成有些方式都是共面的。

考虑一个四元环上的四个中点是合法的,因为可以把它们十字交叉连起来,交于一点(两相交直线共面)。

还有一种是说选择两个点 + 中点 + 任意一个点,其正确性也比较容易证明。

还有一种选两个中点的方式:也就是一个点 \(x\) 连出去两条边 \((x,y)\)\((y,z)\),选择这两条边还有 \(y,z\) 两个点。

最后还有一种 corner case,选三个中点的:三元环的三个中点 + 其上的任意一点。

所以只需要做三元环和四元环计数就好了,时间复杂度 \(O(m\sqrt m)\)

关于三元环计数:按照 \((deg,index)\) 为关键字排序后生成拓扑序,把无向图转成有向图。

然后一个三元环必定是 \(u\rightarrow v,w\) 同时 \(v\rightarrow w\) 的,枚举 \(u\) 后枚举其所有出边 \((u,v)\),然后枚举 \(v\) 的所有出边 \((v,w)\),检查 \(u\rightarrow w\) 是否成立即可,\(O(m\sqrt m)\)

四元环计数用相同的定向手段,枚举其上一点 \(u\),枚举原图里和 \(u\) 相连的一边 \(v\),枚举 \(v\) 的所有出边 \(w\),考虑所有 \(u\rightarrow w\) 的路径任选两条即可,这样我们会发现一个四元环在入度为 \(2\) 的点中被考虑到了一次。但一个四元环可能有两个这样的点。

首先如果只有一个可以发现 \(u\) 的拓扑序会优于 \(w\),所以强制令 \(u\)\(w\) 优,这样就算有两个点,由于他们是相对的,一个做 \(u\) 一个做 \(w\) 统计一次,依旧是 \(O(m\sqrt m)\)

记录

posted on 2023-03-02 09:38  Cry_For_theMoon  阅读(389)  评论(7编辑  收藏  举报