XYD-NOI | 容斥原理

LOJ575 不等关系

考虑只有 \(<\)\(?\)的时候的做法,直接就是集合划分了,也就是 \(\dfrac{(n+1)!}{\prod len_i!}\)

于是直接把大于号容斥掉就行了,替换成 \([?]-[<]\),每次替换成 \(?\) 有一个 \(-1\) 的系数。

考虑带着容斥系数 dp,\(n!\) 最后乘,中间记录 \(\dfrac{1}{len!}\) 的贡献即可。记录一下连续段的长度就可以做了,我们设 \(dp_{i,j}\) 表示考虑了前 \(i\) 个位置,当前 \(<\) 连续段的长度为 \(j\)。如果 \(<\) 的时候,直接在之前的连续段后加上一个数,\(dp_{i,j}\gets dp_{i-1,j-1}\times \dfrac{1}{j}\)。遇到 \(>\) 的时候,我们有两个选择,一个是替换成 \(?\),那么有 \(dp_{i,1}\gets \sum\limits_{j} dp_{i-1,j}\);另一种选择是替换成 \(<\),需要乘上 \(-1\) 的系数,\(dp_{i,j}\gets -dp_{i-1,j-1}\times \dfrac{1}{j}\)

暴力 dp 是 \(O(n^2)\) 的。有第二个维度的存在好像不太好优化,索性直接抛弃掉第二个维度,每次选择极长 \(<\) 段转移,内部钦定了几个 \(>\) 变成 \(<\) 号,就乘以对应的 \(-1\) 次幂。

\[dp_{i}=\sum\limits_{s_j='>'} dp_{j}\times \dfrac{1}{(i-j)!}\times (-1)^{cnt_{i-1}-cnt_j} \]

可以发现这是一个分治 NTT 的形式,可以做到 \(O(n\log^2 n)\)

CF1553I Stairs

可以根据 \(a\) 数组将 \(p\) 划分成 \(m\) 个段,每个段内都是连续上升或者连续下降的。

有一个很直观的式子就是 \(2^mm!\),可是随意放置会导致有些段出现接起来的情况,所以算多了。

考虑弱化版,也就是阶梯必须是升序的,我们可以用容斥,钦定若干个段连起来,最后用 \(m'~!\) 来计算。如果加上降序情况,可以发现一个升序和降序的是不能接在一起的,只有单调性相同的可以拼接,同时注意长度为 \(1\) 的阶梯具有升序和降序的两个性质。假设最后有 \(A\) 个长度 \(>1\) 的,\(B\) 个长度为 \(1\) 的,那么方案数就是 \(2^A(A+B)!\)

有了上述观察,就可以先 dp 一下了,设 \(dp_{i,j}\) 表示考虑了前 \(i\) 个段,目前得到了 \(j\) 个连续段。当 \(b_i=1\)\(i-1=k\) 的时候,\(dp_{i,j}=dp_{i-1,j-1}\),否则有 \(dp_{i,j}=dp_{k,j-1}\times 2\)。最后的答案就是 \(\sum\limits_{i=1}^m(-1)^{m-i}i!dp_{m,i}\)。暴力做是 \(O(n^2)\) 的。

考虑分治,设 \(f_{i,0/1,0/1}\) 表示当前划分了 \(i\) 个连续段,最左的/最右的是否长度为 \(1\)。合并左右两个区间的时候可以发现这是一个卷积形式,直接使用 NTT 即可。时间复杂度 \(O(n\log^2 n)\),由于单次要合并 \(4\times 4\) 个 dp 数组,所以常数很大。

P8340 [AHOI2022] 山河重整

约束条件是 \(\forall k\),对于 \(\le k\) 的数,能凑数 \([1,k]\) 之内的所有数。

\(O(n^2)\) 是很好做的,直接设 \(dp_{i,j}\) 表示用前 \(i\) 个数,凑出了和 \([1,j]\) 的方案数,直接刷表法转移就行了,注意转移的时候需要满足 \(j\ge i\)

直接优化这个 dp 很难做,于是考虑用容斥扔掉若干限制条件。我们先去掉 \(j\ge i\) 的约束,于是直接设 \(f_i\) 表示前 \(i\) 个数满足条件且和恰好为 \(i\) 的方案数。这样子答案就是 \(2^n-f_i\times 2^{n-i-1}\),每个不合法方案会在第一次不合法的时候被删掉。

对于 \(f\) 的转移,还是类似的思想,设 \(g_i\) 表示不需要满足条件单纯用 \([1,i]\) 中的若干个数凑成 \(i\) 的方案数。那么有,\(f_i=g_i-\sum f_j\times val(j,i)\),其中 \(val(j,i)\) 表示用 \([j+2,i]\) 中的数凑出 \(i-j\) 的方案数。

对于 \(g\) 的转移是由于和为 \(n\) 的若干个不同的数最多有 \(O(\sqrt n)\) 个。将整个图画成阶梯状之后做一个完全背包即可,注意选择的数不重复,所有转移约束。

对于 \(val(j,i)\times f_j\) 的转移还是类似于 \(g\) 的,首先贡献不是 \(1\),而是 \(f_j\) 了,而且我们需要加上 \(\ge j+2\) 的基础部分,也就是 \((j+2)\times i\),由于是凑成 \(i-j\),所以还需要手动 \(+j\),那就是 \((j+2)\times i+j\)

注意这个 \(f\) 转移是半在线的,采用倍增优化转移就行了。复杂度是 \(O(\sum \dfrac{n\sqrt n}{2^i})=O(n\sqrt n)\) 的。

P11458 [USACO24DEC] All Pairs Similarity P

如果只有分子的话,就是对于每一位拆贡献就行了。

推广到有分母的情况,还是拆分子贡献,枚举分子每一位把该位置有 \(1\) 的分母放在一起处理。

于是对于分母,我们只需要算出 \(\dfrac{1}{\vert a_i\cup a_j \vert}\) 的贡献就行了,因为对于分子我们已经拆掉贡献了(其实这一步是可以通过 \(\vert a_i\cap a_j\vert=\vert a_i\vert+\vert a_j\vert-\vert a_i\cup a_j\vert\) 直接去掉分母的,不过未必能注意到)。

先是做一步转化,将 \(a_i\) 按位取反,那么贡献也发生了变化 \(\dfrac{1}{\vert a_i\cup a_j \vert}\to \dfrac{1}{k-|a_i\cap a_j|}\),考虑 \(j\to i\) 的贡献,可以发现 \(a_i\cap a_j\)\(a_i\) 的子集,也是 \(a_j\) 的子集。所以贡献法的流程应该是 \(a_j\) 贡献给自己的所有子集,\(a_i\) 再从自己所有的子集中求和。

不过这么做肯定会算重的,具体来说 \(\sum\limits_{s\subset a_i\cap a_j} \dfrac{1}{k-|s|}\) 被我们计算了多次。而我们只希望其贡献出 \(\dfrac{1}{|a_i\cap a_j|}\)。因此设计出一个容斥系数 \(f(|s|)\) 和贡献函数 \(g(|t|)=\dfrac{1}{k-|t|}\),我们希望 \(\sum\limits_{s\subset t}f(|s|)=g(|t|)\),也就是说 \(\sum\limits_{j=0}^i{i\choose j}f_j=g_i\)\(k\) 很小,所以这个式子是可以 \(O(k^2)\) 递推的。

于是我们只需要对于 \(a_j\) 的所有子集 \(s\) 都加上 \(f(|s|)\),再对于 \(a_i\) 的所有子集求和即可。

对于前者就是对于超集做一遍高维前缀和,后者就是对于子集做一遍高维前缀和。问题就做完了。

时间复杂度 \(O(nk+k^22^k)\)

ABC306Ex Balance Scale

假设没有 '=' 的时候,就是一个 DAG 定向了。

需要每次剥离一个独立集,这样子去掉之后还是一个 DAG,就可以 dp 一层一层来解决。按理来说每次必须剥离干净,但是这样子太难转移了,所以只能容斥,系数是 \((-1)^{|T|+1}\)

\[f_{S\cup T}\gets f_S(-1)^{\lvert T\rvert+1} \]

其中 \(T\) 是一个独立集。思考这个系数为何正确,上面说了在一次直接剥离干净的时候直接去掉是正确的,设这个需要被剥离干净的集合为 \(U\),我们枚举的 \(T\),都是其非空子集,我们需要 \(\sum\limits_{T\subset U}f(|T|)=1\)。对于 \(U\) 其大小为奇偶的子集是一样的,由于我们不考虑空集,所以奇子集比偶子集多 \(1\),于是奇数子集产生 \(1\) 的贡献,偶数集合产生 \(-1\) 的贡献就行了。

有 '=' 的时候就是相当于将相邻 "=" 点也就是一个联通块缩成一个点,这样子我们就不要求加入独立集了,加入的点集中有若干联通块,对于单个联通块看成缩成一个点,于是原本系数中的点集大小 \(|T|\) 变成了联通块数。也就是联通块数 \(+1\) 就行了。

P10221 [省选联考 2024] 重塑时光

CF2048G Kevin and Matrices

经典将值域缩小到 \(\{0,1\}\),然后再转化为计算不合法情况就是每行都有 \(1\),且每列都有 \(0\)。很容易算出方案数。

扫描值域,设置 0/1 的做法会算重。

P10064 [SNOI2024] 公交线路

考虑如何判定,对于每个点 \(u\) 求出可以一步到达的所有点集合 \(S_u\),那么要求对于 \(\forall (u,v)~S_u\bigcap S_v\neq \emptyset\),最紧的限制条件是叶子,所以可以将上述判定点的类型设置为叶子。

注意到这是一颗树,且任意两个叶子所对应的联通块之间交集非空,可以推广为所有叶子的交集是一个联通块。

对于联通块计数,可以用点减边容斥。求出每个点合法的方案数之和,减去每条边合法的方案数之和。

直接做的话,在遇到一条路径两个端点都是叶子的时候不好处理,于是再次容斥。钦定不经过若干叶子。对于单点是 \(O(n^2)\) 的。全局 \(O(n^3)\)

每次扔掉一个子树,就可以做到树上背包的复杂度。

反射容斥

相比于卡特兰数的格路行走,这次的要求是不能经过 \(y=x+a\)\(y=x-b\) 两条线。

考虑交替碰到两条线的序列,ABABAB...,BABABA... 和空串。前两者产生 \(0\) 的贡献,后者产生 \(1\) 的贡献。

对于经过 \(A\) 或者 \(B\) 的单条线进行容斥,会多扣除经过 \(AB\) 和 经过 \(BA\) 的方案,加回来经过 \(AB\) 和经过 \(BA\) 的方案,又会多加上经过 \(ABA\) 和经过 \(BAB\) 的方案......

\(AB\) 相交的方案数,就是以 \(A\) 为对称轴翻折终点和线 \(B\),再以翻折过后的 \(B'\) 为对称轴再次对称得到最后的终点。其他 AB 组合也是以此类推。

按照上述做法进行容斥,答案就是 \(\sum\limits_{k\in Z}{n+m\choose n-k(a+b)}-\sum\limits_{k\in Z}{n+m\choose n-k(a+b)+a}\)

注意上述 \(k\) 可以取到负数,而且前后两个式子 \(k\) 的范围是不同的。

可以 \(O(n)\) 预处理,\(O(\dfrac{n}{a+b})\) 进行单组回答。

gym104053J. Math Exam

给定一个长度为 \(n\),值域在 \(\{-1,1\}\) 之间的序列,求任意前缀和在 \([0,m]\) 之间的方案数。

转化一下就是要求行走过程中始终满足 \(x-m\le y\le x\),我们枚举最后的终点共计 \(m\) 个,单次求解复杂度 \(O(\dfrac{n}{m})\),总复杂度就是 \(O(\dfrac{n}{m}\times m)=O(n)\)

posted @ 2025-03-30 21:07  Mirasycle  阅读(75)  评论(0)    收藏  举报