二项式和容斥原理学习笔记 因为这b玩意实在是太难了,所以我得边学边记笔记。
容斥原理# 先从容斥原理开始。
容斥原理的结论如下:
∣ ⋃ i = 1 n S i ∣ = ∑ m = 1 n ( − 1 ) m − 1 ∑ a i < a i − 1 ∣ ⋂ i = 1 m S a i ∣ |\bigcup\limits_{i = 1}^{n}S_{i}| = \sum\limits_{m = 1}^{n}(-1)^{m - 1}\sum\limits_{a_{i} < a_{i - 1}}|\bigcap_{i = 1}^{m}S_{a_{i}}| ∣ i = 1 ⋃ n S i ∣ = m = 1 ∑ n ( − 1 ) m − 1 a i < a i − 1 ∑ ∣ i = 1 ⋂ m S a i ∣ 证明的思路是考虑一个元素在每一个 ⋂ i = 1 m S a i \bigcap\limits_{i = 1}^{m}S_{a_{i}} i = 1 ⋂ m S a i 里出现的次数,然后通过一番暴算,我们能够发现每个元素都只出现了 1 1 1 次,这样,每个元素合起来就变成了总的并集。
详见 OI-wiki
二项式反演# 反演的定义# 现在我们有两个数组:g g g 和 f f f 。而 f f f 和 g g g 之间有对应关系:
f n = ∑ i = 0 n a i × g i f_{n} = \sum\limits_{i = 0}^{n}a_{i} \times g_{i} f n = i = 0 ∑ n a i × g i 然而题目里不可能这么简单,一般是已知 f f f 让我们推 g g g 。这种情况解个方程就可以。但是如果只有解方程的话,还不足以搞出反演 这种东西出来,而上面的柿子在某些情况下会化出许多种优美的形式,所以才搞出了反演这种东西。
二项式反演的公式# 我们可以从容斥原理推到二项式反演的公式。
有全集 U = S 1 ∪ S 2 ∪ S 3 ∪ ⋯ ∪ S n U = S_{1} \cup S_{2} \cup S_{3} \cup \cdots \cup S_{n} U = S 1 ∪ S 2 ∪ S 3 ∪ ⋯ ∪ S n ,且任意 i i i 个集合的交集和并集大小相同。设 f i f_{i} f i 表示任意 i i i 个集合的并集的大小,g i g_{i} g i 表示任意 i i i 个集合的补集的大小。特别的,g 0 = f 0 = ∣ U ∣ g_{0} = f_{0} = |U| g 0 = f 0 = ∣ U ∣ 。根据补集的交集和原集的并集的容斥关系可以得出:
∣ ⋂ i = 1 n S i ∣ = ∣ U ∣ − ∣ ⋃ i = 1 n S i ‾ ∣ = ∣ U ∣ − ( ∣ S 1 ‾ ∣ + ∣ S 2 ‾ ∣ + ⋯ + ( − 1 ) n − 1 ∣ S 1 ‾ ∩ S 2 ‾ ∩ ⋯ ∩ S n ‾ ∣ ) = ∣ U ∣ − ∣ S 1 ‾ ∣ − ∣ S 2 ‾ ∣ − ⋯ + ( − 1 ) n ∣ S 1 ‾ ∩ S 2 ‾ ∩ ⋯ ∩ S n ‾ ∣ = ∑ i = 0 n ( − 1 ) i ( n i ) g i ∣ ⋂ i = 1 n S i ‾ ∣ = ∣ U ∣ − ∣ ⋃ i = 1 n S i ∣ = ∣ U ∣ − ( ∣ S 1 ∣ + ∣ S 2 ∣ + ⋯ + ( − 1 ) n − 1 ∣ S 1 ∩ S 2 ∩ ⋯ ∩ S n ∣ ) = ∣ U ∣ − ∣ S 1 ∣ − ∣ S 2 ∣ − ⋯ + ( − 1 ) n ∣ S 1 ∩ S 2 ∩ ⋯ ∩ S n ∣ ) = ∑ i = 0 n ( − 1 ) i ( n i ) f i \begin{aligned} |\bigcap\limits_{i = 1}^{n}S_{i}| & = |U| - |\bigcup\limits_{i = 1}^{n}\overline{S_{i}}| \\ & = |U| - (|\overline{S_{1}}| + |\overline{S_{2}}| + \cdots + (-1)^{n - 1}|\overline{S_{1}} \cap \overline{S_{2}} \cap \cdots \cap \overline{S_{n}}|) \\ & = |U| - |\overline{S_{1}}| - |\overline{S_{2}}| - \cdots + (-1)^{n}|\overline{S_{1}} \cap \overline{S_{2}} \cap \cdots \cap \overline{S_{n}}| \\ & = \sum\limits_{i = 0}^{n}(-1)^{i}\binom{n}{i}g_{i} \end{aligned} \\ \begin{aligned} |\bigcap\limits_{i = 1}^{n}\overline{S_{i}}| & = |U| - |\bigcup\limits_{i = 1}^{n}S_{i}| \\ & = |U| - (|S_{1}| + |S_{2}| + \cdots + (-1)^{n - 1}|S_{1} \cap S_{2} \cap \cdots \cap S_{n}|) \\ & = |U| - |S_{1}| - |S_{2}| - \cdots + (-1)^{n}|S_{1} \cap S_{2} \cap \cdots \cap S_{n}|) \\ & = \sum\limits_{i = 0}^{n}(-1)^{i}\binom{n}{i}f_{i} \end{aligned} ∣ i = 1 ⋂ n S i ∣ = ∣ U ∣ − ∣ i = 1 ⋃ n S i ∣ = ∣ U ∣ − ( ∣ S 1 ∣ + ∣ S 2 ∣ + ⋯ + ( − 1 ) n − 1 ∣ S 1 ∩ S 2 ∩ ⋯ ∩ S n ∣ ) = ∣ U ∣ − ∣ S 1 ∣ − ∣ S 2 ∣ − ⋯ + ( − 1 ) n ∣ S 1 ∩ S 2 ∩ ⋯ ∩ S n ∣ = i = 0 ∑ n ( − 1 ) i ( i n ) g i ∣ i = 1 ⋂ n S i ∣ = ∣ U ∣ − ∣ i = 1 ⋃ n S i ∣ = ∣ U ∣ − ( ∣ S 1 ∣ + ∣ S 2 ∣ + ⋯ + ( − 1 ) n − 1 ∣ S 1 ∩ S 2 ∩ ⋯ ∩ S n ∣ ) = ∣ U ∣ − ∣ S 1 ∣ − ∣ S 2 ∣ − ⋯ + ( − 1 ) n ∣ S 1 ∩ S 2 ∩ ⋯ ∩ S n ∣ ) = i = 0 ∑ n ( − 1 ) i ( i n ) f i 然而,我们惊奇的发现:∣ ⋂ i = 1 n S i ∣ = f n , ∣ ⋂ i = 1 n S i ‾ ∣ = g n |\bigcap\limits_{i = 1}^{n}S_{i}| = f_{n},|\bigcap\limits_{i = 1}^{n}\overline{S_{i}}| = g_{n} ∣ i = 1 ⋂ n S i ∣ = f n , ∣ i = 1 ⋂ n S i ∣ = g n ,于是我们便得到了二项式反演的第一个公式:
f n = ∑ i = 0 n ( − 1 ) i ( n i ) g i ⟺ g n = ∑ i = 0 n ( − 1 ) i ( n i ) f i f_{n} = \sum\limits_{i = 0}^{n}(-1)^{i}\binom{n}{i}g_{i} \Longleftrightarrow g_{n} = \sum\limits_{i = 0}^{n}(-1)^{i}\binom{n}{i}f_{i} f n = i = 0 ∑ n ( − 1 ) i ( i n ) g i ⟺ g n = i = 0 ∑ n ( − 1 ) i ( i n ) f i 如何用数学方法证明这个公式是正确的呢?直接代入就行。 将 f n = ∑ i = 0 n ( − 1 ) i ( n i ) g i f_{n} = \sum\limits_{i = 0}^{n}(-1)^{i}\binom{n}{i}g_{i} f n = i = 0 ∑ n ( − 1 ) i ( i n ) g i 代入:
g n = ∑ i = 0 n ( − 1 ) i ( n i ) ∑ j = 0 i ( − 1 ) j ( i j ) g j = ∑ i = 0 n ∑ j = 0 i ( − 1 ) i + j ( n i ) ( i j ) g j = ∑ i = 0 n ∑ j = 0 i ( − 1 ) i + j ( n j ) ( n − j n − i ) g j = ∑ j = 0 n ( n j ) g j ∑ i = 0 n − j ( − 1 ) i ( n − j i ) = ∑ j = 0 n ( n j ) g j [ j = n ] = g n \begin{aligned} g_{n} & = \sum\limits_{i = 0}^{n}(-1)^{i}\binom{n}{i}\sum\limits_{j = 0}^{i}(-1)^{j}\binom{i}{j}g_{j} \\ & = \sum\limits_{i = 0}^{n}\sum\limits_{j = 0}^{i}(-1)^{i + j}\binom{n}{i}\binom{i}{j}g_{j} \\ & = \sum\limits_{i = 0}^{n}\sum\limits_{j = 0}^{i}(-1)^{i + j}\binom{n}{j}\binom{n - j}{n - i}g_{j} \\ & = \sum\limits_{j = 0}^{n}\binom{n}{j}g_{j}\sum\limits_{i = 0}^{n - j}(-1)^{i}\binom{n - j}{i} \\ & = \sum\limits_{j = 0}^{n}\binom{n}{j}g_{j}[j = n] \\ & = g_{n} \end{aligned} g n = i = 0 ∑ n ( − 1 ) i ( i n ) j = 0 ∑ i ( − 1 ) j ( j i ) g j = i = 0 ∑ n j = 0 ∑ i ( − 1 ) i + j ( i n ) ( j i ) g j = i = 0 ∑ n j = 0 ∑ i ( − 1 ) i + j ( j n ) ( n − i n − j ) g j = j = 0 ∑ n ( j n ) g j i = 0 ∑ n − j ( − 1 ) i ( i n − j ) = j = 0 ∑ n ( j n ) g j [ j = n ] = g n 显然成立。当然,这个柿子里还有一些不那么显然的东西
( n i ) ( i j ) = ( n j ) ( n − j n − i ) \displaystyle\binom{n}{i}\binom{i}{j} = \binom{n}{j}\binom{n - j}{n - i} ( i n ) ( j i ) = ( j n ) ( n − i n − j ) 这个可以用数学方法证明,也可组合意义的方法证明,这里主要说明组合意义的证法。 求 ∣ U ∣ = n , ∣ A ∣ = i , b = ∣ j ∣ , B ⊆ A ⊆ U |U| = n,|A| = i,b = |j|,B \subseteq A \subseteq U ∣ U ∣ = n , ∣ A ∣ = i , b = ∣ j ∣ , B ⊆ A ⊆ U 的方案数。这里有两种方法: 法一:先在 U U U 里取 i i i 个元素构成集合 A A A ,方案数为 ( n i ) \binom{n}{i} ( i n ) ,然后在 i i i 个元素里再取 j j j 个元素构成集合 B B B ,方案数为 ( i j ) \binom{i}{j} ( j i ) ,合起来就是 ( n i ) ( i j ) \binom{n}{i}\binom{i}{j} ( i n ) ( j i ) 法二:先在 U U U 里取 j j j 个元素构成集合 B B B ,方案数为 ( n j ) \binom{n}{j} ( j n ) ,再在剩下的 n − j n - j n − j 个元素里取 i − j i - j i − j 个元素,方案数为 ( n − j i − j ) \binom{n - j}{i - j} ( i − j n − j ) ,又有 ( n − j i − j ) = ( n − j n − i ) \binom{n - j}{i - j} = \binom{n - j}{n - i} ( i − j n − j ) = ( n − i n − j ) ,所以方案数就是 ( n j ) ( n − j n − i ) \binom{n}{j}\binom{n - j}{n - i} ( j n ) ( n − i n − j ) 。 得证。∑ i = 0 n − j ( − 1 ) i ( n − j i ) = [ j = n ] \displaystyle\sum\limits_{i = 0}^{n - j}(-1)^{i}\binom{n - j}{i} = [j = n] i = 0 ∑ n − j ( − 1 ) i ( i n − j ) = [ j = n ] 设 k = n − j k = n - j k = n − j ,令 k = 0 k = 0 k = 0 ,原式的值为 1 1 1 ,令 k > 0 k > 0 k > 0 ,原式为 ∑ i = 0 k ( k i ) ( − 1 ) i \sum\limits_{i = 0}^{k}\binom{k}{i}(-1)^{i} i = 0 ∑ k ( i k ) ( − 1 ) i ,我们来添一个项:∑ i = 0 k ( k i ) ( − 1 ) i 1 i \sum\limits_{i = 0}^{k}\binom{k}{i}(-1)^{i} 1^{i} i = 0 ∑ k ( i k ) ( − 1 ) i 1 i ,发现,这玩意不就是二项式定理的 的右边那坨吗?那么原式就是 ( − 1 + 1 ) k = 0 k = 0 (-1 + 1)^{k} = 0^{k} = 0 ( − 1 + 1 ) k = 0 k = 0 。得证。现在再看那个式子就比较好懂了。注意再第二点那里,因为数学老师告诉我们 0 0 0^{0} 0 0 没有意义,所以在证明的时候为了严谨需要分类讨论。在做题的时候,为了方便可以钦定 0 0 = 1 0^{0} = 1 0 0 = 1 。
二项式反演的 4 4 4 种形式:
形式一
f n = ∑ i = 0 n ( − 1 ) i ( n i ) g i ⟺ g n = ∑ i = 0 n ( − 1 ) i ( n i ) f i f_{n} = \sum\limits_{i = 0}^{n}(-1)^{i}\binom{n}{i}g_{i} \Longleftrightarrow g_{n} = \sum\limits_{i = 0}^{n}(-1)^{i}\binom{n}{i}f_{i} f n = i = 0 ∑ n ( − 1 ) i ( i n ) g i ⟺ g n = i = 0 ∑ n ( − 1 ) i ( i n ) f i 形式二(形式一的变形,比较常用):
f n = ∑ i = 0 n ( n i ) g i ⟺ g n = ∑ i = 0 n ( − 1 ) n − i ( n i ) f i f_{n} = \sum\limits_{i = 0}^{n}\binom{n}{i}g_{i} \Longleftrightarrow g_{n} = \sum\limits_{i = 0}^{n}(-1)^{n - i}\binom{n}{i}f_{i} f n = i = 0 ∑ n ( i n ) g i ⟺ g n = i = 0 ∑ n ( − 1 ) n − i ( i n ) f i 形式三:
f n = ∑ i = n m ( − 1 ) i ( i n ) g i ⟺ g n = ∑ i = n m ( − 1 ) i ( i n ) f i f_{n} = \sum\limits_{i = n}^{m}(-1)^{i}\binom{i}{n}g_{i} \Longleftrightarrow g_{n} = \sum\limits_{i = n}^{m}(-1)^{i}\binom{i}{n}f_{i} f n = i = n ∑ m ( − 1 ) i ( n i ) g i ⟺ g n = i = n ∑ m ( − 1 ) i ( n i ) f i 形式四(形式三的变形,非常常用):
f n = ∑ i = n m ( i n ) g i ⟺ g n = ∑ i = n m ( − 1 ) i − n ( i n ) f i f_{n} = \sum\limits_{i = n}^{m}\binom{i}{n}g_{i} \Longleftrightarrow g_{n} = \sum\limits_{i = n}^{m}(-1)^{i - n}\binom{i}{n}f_{i} f n = i = n ∑ m ( n i ) g i ⟺ g n = i = n ∑ m ( − 1 ) i − n ( n i ) f i 为什么形式四非常常用呢?因为题里多半都是求恰好 满足一些条件的方案数。而我们好求的一般都是钦定 满足一些条件的方案数。比如:有 m m m 个条件,g i g_{i} g i 表示恰好满足 i i i 个条件的方案数,f i f_{i} f i 表示钦定满足 i i i 个条件,剩下的随便的方案数。从 f i f_{i} f i 的表述里就可以看出,f i f_{i} f i 比 g i g_{i} g i 好求很多,我们就可以用 f f f 推得 g g g 。
推导思路和证明思路来自 __allenge 的博客 。
例题 for 暴力容斥# 我们先考虑没有限制的情况:d p i dp_{i} d p i 表示没有限制的情况下买价格为 i i i 的物品的方案数,容易发现这就是一个完全背包,可以在 O ( v a l ) \mathcal{O}(val) O ( v a l ) 的复杂度下预处理,其中 v a l val v a l 表示最大价格。然后接下来考虑限制的情况。发现正着考虑不超过限制的情况不太好做,正难则反 。 我们考虑超过限制的情况。超过限制就是第 i i i 个硬币强制选 d i + 1 d_{i} + 1 d i + 1 个,剩下的依然随便选,那么情况数就是 d p s − ( d i + 1 ) × c i dp_{s - (d_{i} + 1) \times c_{i}} d p s − ( d i + 1 ) × c i ,答案就要减去 ∑ i = 1 4 d p s − ( d i + 1 ) × c i \sum\limits_{i = 1}^{4}dp_{s - (d_{i} + 1) \times c_{i}} i = 1 ∑ 4 d p s − ( d i + 1 ) × c i 。这是单个硬币超出限制的情况,如果有多个硬币,就得请出我们的容斥原理了。但是,容斥原理的公式要求任意两个集合的交集和并集大小相等,这很明显不相等啊,我们又发现:只有 4 4 4 种硬币,那我们直接枚举不就行了吗?无非就是带一个 16 16 16 的常数嘛。
核心代码:
1
2
3
4
5
6
7
8
9
10
int ans = dp[s]; //dp是预处理好的完全背包
for ( int k = 1 ; k < ( 1 << 4 ); k ++ )
{
int tmp = s;
for ( int i = 0 ; i < 4 ; i ++ )
if (k & ( 1 << i))
tmp -= (d[i] + 1 ) * c[i];
if (tmp >= 0 )
ans += ( __builtin_popcount (k) & - 1 : 1 ) * dp[tmp]; //容斥
}
发现考虑不同的方案数有点难,正难则反 。 我们设 c n t i cnt_i c n t i 为钦定有 i i i 位相同的方案数。这个可以用哈希求解。我们设哈希函数:
1
2
3
4
int hsh ( char ch ){ return isdigit (ch) ? ch - '0' : ch - 'a' + 10 ;}
int hsh ( char ch1 , char ch2 ){ return hsh (ch1) * 36 + hsh (ch2);}
int hsh ( char ch1 , char ch2 , char ch3 )
{ return hsh (ch1) * 36 * 36 + hsh (ch2) * 36 + hsh (ch3);}
分别用于求解一个字符,两个字符,三个字符的哈希值。很明显,这个哈希函数生成的哈希值不会超过 6 × 10 4 6 \times 10 ^ 4 6 × 1 0 4 。那么我们记录 t m p i tmp_{i} t m p i 表示有多少个字符序列的哈希值是 i i i ,然后要求解方案数呢,就相当于求解在 t m p i tmp_{i} t m p i 个元素里随便取两个不同元素的方案数 ,很明显是 t m p i × ( t m p i − 1 ) 2 \frac{tmp_{i} \times (tmp_{i} - 1)}{2} 2 t m p i × ( t m p i − 1 ) 。最后,我们求得了钦定有 i i i 个元素相同的方案数,因为最多有 4 4 4 个元素相同,所以我们直接暴力容斥就行。
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
for ( int x = 1 ; x <= 4 ; x ++ ) //一个字符相同的
{
memset (tmp, 0 ,sizeof tmp);
for ( int i = 1 ; i <= n; i ++ )
tmp[ hsh (s[i][x])] ++ ;
for ( int i = 0 ; i <= 6 e 4 ; i ++ )
cnt[ 1 ] += (tmp[i] * (tmp[i] - 1 )) >> 1 ;
}
for ( int x = 1 ; x <= 4 ; x ++ ) //两个字符相同
{
for ( int y = x + 1 ; y <= 4 ; y ++ )
{
memset (tmp, 0 ,sizeof tmp);
for ( int i = 1 ; i <= n; i ++ )
tmp[ hsh (s[i][x],s[i][y])] ++ ;
for ( int i = 0 ; i <= 6 e 4 ; i ++ )
cnt[ 2 ] += (tmp[i] * (tmp[i] - 1 )) >> 1 ;
}
}
for ( int x = 1 ; x <= 4 ; x ++ ) //三个字符相同
{
for ( int y = x + 1 ; y <= 4 ; y ++ )
{
for ( int z = y + 1 ; z <= 4 ; z ++ )
{
memset (tmp, 0 ,sizeof tmp);
for ( int i = 1 ; i <= n; i ++ )
tmp[ hsh (s[i][x],s[i][y],s[i][z])] ++ ;
for ( int i = 0 ; i <= 6 e 4 ; i ++ )
cnt[ 3 ] += (tmp[i] * (tmp[i] - 1 )) >> 1 ;
}
}
}
//以下是暴力容斥
ans[ 3 ] = cnt[ 3 ];
ans[ 2 ] = cnt[ 2 ] - 3 * ans[ 3 ];
ans[ 1 ] = cnt[ 1 ] - ans[ 2 ] * 2 - ans[ 3 ] * 3 ;
ans[ 0 ] = ((n * (n - 1 )) >> 1 ) - ans[ 1 ] - ans[ 2 ] - ans[ 3 ];
printf ( " %lld " ,ans[ 4 - d]); //由于求解的是相同的,所以不同的就是4 - d个
例题 for 二项式反演# 设 f m f_{m} f m 表示钦定有 m m m 个 2023
的方案数。用插板法 ,在这些 2023
的缝里插入那些乱七八糟的数。在 m m m 个 2023
中插入 n − 4 m n - 4m n − 4 m 个数的方案是
f m = ( ( n − 4 m ) + ( m + 1 ) − 1 ( n − 4 m ) − 1 ) f_{m} = \binom{(n - 4m) + (m + 1) - 1}{(n - 4m) - 1} f m = ( ( n − 4 m ) − 1 ( n − 4 m ) + ( m + 1 ) − 1 ) 设 g m g_{m} g m 表示恰好有 m m m 个 2023
的方案数,那么有:
f m = ∑ i = m n ( i m ) g i f_{m} = \sum\limits_{i = m}^{n}\binom{i}{m}g_{i} f m = i = m ∑ n ( m i ) g i 根据二项式反演公式,我们得到:
g m = ∑ i = m n ( − 1 ) i − m ( i m ) f i g_{m} = \sum\limits_{i = m}^{n}(-1)^{i - m}\binom{i}{m}f_{i} g m = i = m ∑ n ( − 1 ) i − m ( m i ) f i 那么我们就求得了 g m g_{m} g m ,就是答案。时间复杂度 O ( n ) \mathcal{O}(n) O ( n )
设 f k f_{k} f k 表示钦定有 k k k 个相同元素的情况,那么,剩下的 n − k n - k n − k 个元素能够组成 2 n − k 2^{n - k} 2 n − k 个集合,而,这 2 n − k 2^{n - k} 2 n − k 个集合又能够再组成 2 2 n − k 2^{2^{n - k}} 2 2 n − k 个集合。那么显然有
f k = ( n k ) 2 2 n − k f_{k} = \binom{n}{k}2^{2^{n - k}} f k = ( k n ) 2 2 n − k 设 g k g_{k} g k 表示恰好有 k k k 个相同元素的情况,那么有:
f k = ∑ i = k n ( i k ) g i f_{k} = \sum\limits_{i = k}^{n}\binom{i}{k}g_{i} f k = i = k ∑ n ( k i ) g i 反演一下得到:
g k = ∑ i = k n ( − 1 ) i − k ( i k ) f i g_{k} = \sum\limits_{i = k}^{n}(-1)^{i - k}\binom{i}{k}f_{i} g k = i = k ∑ n ( − 1 ) i − k ( k i ) f i 我们就得到了 g k g_{k} g k 。时间复杂度 O ( n ) \mathcal{O}(n) O ( n ) 。
首先设 f i f_{i} f i 表示钦定 有 i i i 个同学没有拿到特产的方案数,那么有:
f i = ( n i ) × ∏ j m ( a j + n − i − 1 n − i − 1 ) f_{i} = \binom{n}{i} \times \prod\limits_{j}^{m}\binom{a_{j} + n - i - 1}{n - i - 1} f i = ( i n ) × j ∏ m ( n − i − 1 a j + n − i − 1 ) 后面那坨表示将 a j a_{j} a j 个物品分成 n − i n - i n − i 个可空集的方案数,而前面的是因为我们并没有钦定哪几个人没有,所以要乘一个在 n n n 个人里面选 i i i 个的方案数。
接下来我们开始反演。设 g i g_{i} g i 表示正好有 i i i 个人没有,那么有反演公式:
g i = ∑ j = i n ( − 1 ) j − i ( j i ) f j g_{i} = \sum\limits_{j = i}^{n}(-1)^{j - i}\binom{j}{i}f_{j} g i = j = i ∑ n ( − 1 ) j − i ( i j ) f j 而我们要让每一个人都拿到,答案就是 g 0 g_{0} g 0 。这样,我们就做完了。时间复杂度 O ( n ) \mathcal{O}(n) O ( n )
依然是正难则反 + 二项式反演 演都不带演
设 f i f_{i} f i 表示钦定有 i i i 个位置相同,g i g_{i} g i 表示正好 i i i 个位置相同。那么有反演:
f i = ∑ j = n m ( j n ) g j ⟺ g i = ∑ j = n m ( − 1 ) i − n ( j n ) f j f_{i} = \sum\limits_{j = n}^{m}\binom{j}{n}g_{j} \Longleftrightarrow g_{i} = \sum\limits_{j = n}^{m}(-1)^{i - n}\binom{j}{n}f_{j} f i = j = n ∑ m ( n j ) g j ⟺ g i = j = n ∑ m ( − 1 ) i − n ( n j ) f j 我们的答案就是 g 0 g_{0} g 0
现在的问题就在于怎么求 f i f_{i} f i 。发现初始数组的顺序对于答案来说没有影响,那么我们就记录每种颜色的数量并去重,然后开始 dp。设 d p i , j dp_{i,j} d p i , j 表示循环到第 i i i 个颜色,钦定有 j j j 个元素相同的方案数。那么有
d p i , j = ∑ k = 0 min ( c n t i , t m p ) d p i − 1 , j − k × ( c n t i k ) × ( t m p − j c n t i − k ) dp_{i,j} = \sum\limits_{k = 0}^{\min(cnt_{i},tmp)}dp_{i - 1,j - k} \times \binom{cnt_{i}}{k} \times \binom{tmp - j}{cnt_{i} - k} d p i , j = k = 0 ∑ m i n ( c n t i , t m p ) d p i − 1 , j − k × ( k c n t i ) × ( c n t i − k t m p − j ) 初学时属实没想到这玩意还能带 dp
很明显,f i = d p m , i f_{i} = dp_{m,i} f i = d p m , i ,其中 m m m 表示颜色总数。然后我们就得出了答案,时间复杂度为 O ( n 2 ) \mathcal{O}(n^2) O ( n 2 ) 。
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
sort (a + 1 ,a + n + 1 );
int m = unique (a + 1 ,a + n + 1 ) - a - 1 ,tmp = 0 ;
dp[ 0 ][ 0 ] = 1 ;
for ( int i = 1 ; i <= m; i ++ )
{
tmp += cnt[a[i]];
for ( int j = 0 ; j <= tmp; j ++ )
for ( int k = 0 ; k <= min (j,cnt[a[i]]); k ++ )
dp[i][j] = (dp[i][j] + dp[i - 1 ][j - k] * c (tmp - j,cnt[a[i]] - k) % mod * c (cnt[a[i]],k) % mod) % mod;
}
int ans = 0 ;
for ( int i = 0 ; i <= n; i ++ )
ans = ((ans + (i & 1 ? - 1 : 1 ) * dp[m][i]) % mod + mod) % mod;
好题推荐# P4448 [AHOI2018 初中组] 球球的排列 P3158 [CQOI2011] 放棋子 P3160 [CQOI2012] 局部极小值