# 『容斥原理和广义容斥原理』

<更新提示>

<第一次更新>

<正文>

## 容斥原理

### 基础概念

$\left |\bigcap_{i=1}^n\overline{A_i}\right|=|S|+\sum_{T\subseteq\{1,2,...,n\},T\not =\emptyset}(-1)^{|T|}\left | \bigcap_{i\in T}A_i \right |$

$\left | \bigcup_{i=1}^nA_i \right |=\sum_{T\subseteq \{1,2,...,n\}}(-1)^{|T|-1}\left | \bigcap_{i\in T} A_i \right |$

### 毒瘤选举

#### Sample Input

1
4 3


#### Sample Output

36


#### 解析

$\binom{k}{m}(k-m)^n$

$k^n+\sum_{i=1}^m(-1)^i\binom{k}{i}(k-i)^n$

$Code:$

#include <bits/stdc++.h>
using namespace std;
const int N = 190000 , p[] = {0,3,19,97,180547};
int inv[5][N],fac[5][N],ans[5],n,m;
inline int add(int a,int b,int Mod) { return a + b >= Mod ? a + b - Mod : a + b; }
inline int mul(int a,int b,int Mod) { return 1LL * a * b % Mod; };
inline int sub(int a,int b,int Mod) { return a - b < 0 ? a - b + Mod : a - b; }
inline void Add(int &a,int b,int Mod) { a = add( a , b , Mod ); }
inline void Mul(int &a,int b,int Mod) { a = mul( a , b , Mod ); }
inline void Sub(int &a,int b,int Mod) { a = sub( a , b , Mod ); }
inline int quickpow(int a,int b,int Mod)
{
int res = 1;
for ( ; b ; Mul(a,a,Mod) , b>>=1 )
if ( 1 & b ) Mul(res,a,Mod);
return res;
}
inline void init(void)
{
for (int k=1;k<=4;k++)
{
fac[k][0] = inv[k][0] = 1;
for (int i=1;i<p[k];i++)
fac[k][i] = mul( fac[k][i-1] , i , p[k] );
inv[k][p[k]-1] = quickpow( fac[k][p[k]-1] , p[k]-2 , p[k] );
for (int i=p[k]-2;i>=1;i--)
inv[k][i] = mul( inv[k][i+1] , i+1 , p[k] );
}
}
inline int C(int n,int m,int k)
{
if ( n < m || n < 0 || m < 0 ) return 0;
int res = fac[k][n];
return mul( res , mul( inv[k][m] , inv[k][n-m] , p[k] ) , p[k] );
}
inline int Lucas(int n,int m,int k)
{
if ( m == 0 ) return 1;
int res = Lucas( n/p[k] , m/p[k] , k );
return mul( res , C( n%p[k] , m%p[k] , k ) , p[k] );
}
inline int Exeuclid(int a,int b,int &x,int &y)
{
if ( b == 0 ) return x = 1 , y = 0 , a;
int t = Exeuclid( b , a%b , x , y );
int _x = x , _y = y;
x = _y , y = _x - a / b * _y;
return t;
}
inline int ExCRT(void)
{
int M = p[1] , res = ans[1] % M , t , k , x , y;
for (int i=2;i<=4;i++)
{
k = ( (ans[i]-res) % p[i] + p[i] ) % p[i];
t = Exeuclid( M , p[i] , x , y );
x = x * (k/t) % (p[i]/t);
res = res + x * M;
M = M * p[i] / t;
res = ( res % M + M ) % M;
}
return res;
}
inline int solve(void)
{
for (int k=1;k<=4;k++)
{
ans[k] = quickpow( m , n , p[k] );
for (int i=1;i<=m;i++)
{
int val = mul( Lucas(m,i,k) , quickpow(m-i,n,p[k]) , p[k] );
if ( i & 1 ) Sub( ans[k] , val , p[k] );
else Add( ans[k] , val , p[k] );
}
}
}
int main(void)
{
init();
int T; long long N,M;
scanf("%d",&T);
while ( T --> 0 )
{
scanf("%lld%lld",&N,&M);
if ( M >= 180547 ) puts("0");
else n = N , m = M , solve(),
printf("%d\n",ExCRT());
}
return 0;
}


### 小w的喜糖

#### Sample Input

6
1
1
2
2
3
3


#### Sample Output

10


#### 解析

$f[i][j]$代表前$i$种糖，有$j$个人拿着自己同种糖的方案数，可以直接转移：

$f[i][j]=\sum_{k=0}^{min(j,cnt[i])}f[i-1][j-k]\times\binom{cnt[i]}{k}\times cnt[i]^{\underline{k}}$

$m$为颜色总数，然后容斥：

$ans=\sum_{i=0}^m(-1)^i\times f[m][i]\times (n-i)!$

$Code:$

#include <bits/stdc++.h>
using namespace std;
const int N = 2020 , Mod = 1e9+9;
int n,m,a[N],cnt[N],f[N][N],fac[N],inv[N],ans;
inline int add(int a,int b) { return a + b >= Mod ? a + b - Mod : a + b; }
inline int mul(int a,int b) { return 1LL * a * b % Mod; };
inline int sub(int a,int b) { return a - b < 0 ? a - b + Mod : a - b; }
inline void Add(int &a,int b) { a = add( a , b ); }
inline void Mul(int &a,int b) { a = mul( a , b ); }
inline void Sub(int &a,int b) { a = sub( a , b ); }
inline int quickpow(int a,int b) { int res = 1; for (;b;Mul(a,a),b>>=1) if ( 1 & b ) Mul(res,a); return res; }
inline void input(void)
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
cnt[a[i]]++;
}
}
inline void init(void)
{
fac[0] = inv[0] = 1;
for (int i=1;i<=n;i++)
fac[i] = mul( fac[i-1] , i );
inv[n] = quickpow( fac[n] , Mod-2 );
for (int i=n-1;i>=1;i--)
inv[i] = mul( inv[i+1] , i+1 );
sort( cnt+1 , cnt+n+1 );
reverse( cnt+1 , cnt+n+1 );
for (int i=1;i<=n+1;i++)
if ( cnt[i] == 0 ) { m = i-1 ; break; }
}
inline int C(int n,int m) { return mul( fac[n] , mul( inv[m] , inv[n-m] ) ); }
inline int A(int n,int m) { return mul( fac[n] , inv[n-m] ); }
inline void DynamicProgram(void)
{
f[0][0] = 1;
for (int i=1,lim=cnt[1];i<=m;lim+=cnt[++i])
for (int j=0;j<=lim;j++)
for (int k=0;k<=min(cnt[i],j);k++)
Add( f[i][j] , mul( f[i-1][j-k] , mul( C(cnt[i],k) , A(cnt[i],k) ) ) );
}
inline void solve(void)
{
for (int i=0;i<=n;i++)
if ( i & 1 ) Sub( ans , mul( f[m][i] , fac[n-i] ) );
else Add( ans , mul( f[m][i] , fac[n-i] ) );
for (int i=1;i<=m;i++)
Mul( ans , inv[cnt[i]] );
}
int main(void)
{
input();
init();
DynamicProgram();
solve();
printf("%d\n",ans);
return 0;
}



## 广义容斥原理

### 容斥方法

$\alpha(0)=|S|\\ \ \\ \alpha(1)=\sum_{i}|A_i|\\ \ \\ \alpha(2)=\sum_{i,j}|A_i\cap A_j| \\ ...\\ \ \\ \alpha(k)=\sum_{T\subseteq\{1,2,...,n\},|T|=k}\left | \bigcap_{i\in T}A_i \right |$

$\beta(k)=\alpha(k)-\sum_{i=k+1}^n\binom{i}{k}\beta(i)$

$\beta(k)=\sum_{i=k}^n(-1)^{i-k}\binom{i}{k}\alpha(i)$

$1.$ 如果$t<k$，则该元素一次都没有被计算过。

$2.$ 如果$t=k$，则该元素恰好被计算了一次。

$3.$ 如果$t>k$$......$真复杂

$\sum_{i=k}^t(-1)^{i-k}\binom{i}{k}\binom{t}{i}$

$\sum_{i=k}^t(-1)^{i-k}\binom{i}{k}\binom{t}{i}\\ \ \\ =\sum_{i=k}^t(-1)^{i-k}\binom{t}{k}\binom{t-k}{i-k}\\ \ \\ = \binom{t}{k}\sum_{i=k}^t(-1)^{i-k}\binom{t-k}{i-k}\\ \ \\ = \binom{t}{k}\sum_{i=0}^{t-k}(-1)^{i}\binom{t-k}{i}$

$(1-x)^{t-k}=\sum_{i=0}^{t-k}(-x)^i\binom{t-k}{i}$

$x=1$，我们得知：

$\sum_{i=0}^{t-k}(-1)^{i}\binom{t-k}{i}=0$

### 集合计数

#### Sample Input

3 2


#### Sample Output

6


#### 解析

$\alpha(k)=\binom{n}{k}2^{2^{n-k}}$

$Code:$

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6+20 , Mod = 1e9+7;
int n,k,alpha[N],fac[N],inv[N],Pow[N],ans;
inline int add(int a,int b) { return a + b >= Mod ? a + b - Mod : a + b; }
inline int mul(int a,int b) { return 1LL * a * b % Mod; };
inline int sub(int a,int b) { return a - b < 0 ? a - b + Mod : a - b; }
inline void Add(int &a,int b) { a = add( a , b ); }
inline void Mul(int &a,int b) { a = mul( a , b ); }
inline void Sub(int &a,int b) { a = sub( a , b ); }
inline int quickpow(int a,int b) { int res = 1; for (;b;Mul(a,a),b>>=1) if ( 1 & b ) Mul(res,a); return res; }
inline void init(void)
{
fac[0] = inv[0] = Pow[0] = 1;
for (int i=1;i<=n;i++)
fac[i] = mul( fac[i-1] , i ) , Pow[i] = Pow[i-1] * 2LL % (Mod-1);
inv[n] = quickpow( fac[n] , Mod-2 );
for (int i=n-1;i>=1;i--)
inv[i] = mul( inv[i+1] , i+1 );
}
inline int C(int n,int m) { return mul( fac[n] , mul( inv[m] , inv[n-m] ) ); }
inline void solve(void)
{
for (int i=0;i<=n;i++)
alpha[i] = mul( C(n,i) , quickpow( 2 , Pow[n-i] ) );
for (int i=k;i<=n;i++)
if ( ( i - k ) & 1 ) Sub( ans , mul( C(i,k) , alpha[i] ) );
else Add( ans , mul( C(i,k) , alpha[i] ) );
}
int main(void)
{
scanf("%d%d",&n,&k);
init();
solve();
printf("%d\n",ans);
return 0;
}


### 已经没有什么好害怕的了

#### Sample Input

4 2
5 35 15 45
40 20 10 30


#### Sample Output

4


#### 解析

$\begin{cases}x+y=n\\ x-y=k\end{cases}$

$f[i][j]=f[i-1][j]+f[i-1][j-1]\times (cnt[i]-(j-1))$

$Code:$

#include <bits/stdc++.h>
using namespace std;
const int N = 2020 , Mod = 1e9+9;
int n,k,a[N],b[N],C[N][N],fac[N];
int alpha[N],f[N][N],cnt[N],ans;
inline int add(int a,int b) { return a + b >= Mod ? a + b - Mod : a + b; }
inline int mul(int a,int b) { return 1LL * a * b % Mod; };
inline int sub(int a,int b) { return a - b < 0 ? a - b + Mod : a - b; }
inline void Add(int &a,int b) { a = add( a , b ); }
inline void Mul(int &a,int b) { a = mul( a , b ); }
inline void Sub(int &a,int b) { a = sub( a , b ); }
inline int quickpow(int a,int b) { int res = 1; for (;b;Mul(a,a),b>>=1) if ( 1 & b ) Mul(res,a); return res; }
inline void input(void)
{
scanf("%d%d",&n,&k);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=1;i<=n;i++) scanf("%d",&b[i]);
}
inline void init(void)
{
int p = 0; fac[0] = 1;
for (int i=1;i<=n;i++)
{
fac[i] = mul( fac[i-1] , i );
while ( p < n && b[p+1] < a[i] ) p++;
cnt[i] = p;
}
C[0][0] = C[1][0] = C[1][1] = 1;
for (int i=2;i<=n;i++)
{
C[i][0] = C[i][i] = 1;
for (int j=1;j<i;j++)
C[i][j] = add( C[i-1][j-1] , C[i-1][j] );
}
}
inline void DynamicProgram(void)
{
for (int i=0;i<=n;i++) f[i][0] = 1;
for (int i=1;i<=n;i++)
for (int j=1;j<=i;j++)
f[i][j] = add( f[i-1][j] , mul( f[i-1][j-1] , max( cnt[i]-j+1 , 0 ) ) );
}
inline void solve(void)
{
k = ( n + k ) / 2;
for (int i=1;i<=n;i++)
alpha[i] = mul( f[n][i] , fac[n-i] );
for (int i=k;i<=n;i++)
if ( ( i - k ) & 1 ) Sub( ans , mul( C[i][k] , alpha[i] ) );
else Add( ans , mul( C[i][k] , alpha[i] ) );
}
int main(void)
{
input();
if ( ( n + k ) & 1 )
return puts("0") , 0;
sort( a+1 , a+n+1 );
sort( b+1 , b+n+1 );
init();
DynamicProgram();
solve();
printf("%d\n",ans);
return 0;
}



<后记>

posted @ 2019-09-16 22:18  Parsnip  阅读(1357)  评论(1编辑  收藏  举报