二项式反演-斯特林反演-或计数-学习笔记
i207M
2019-01-11 07:16:26
# 对于反演,有一个通用的形式
以二项式反演为例,因为$\sum \limits_{i=0}^n (-1)^{i+k}C_n^iC_i^k=[n=k]$,设矩阵$P:P[i][j]=C_i^j;Q:Q[i][j]=(-1)^{i+j}C_i^j$,则有$PQ=I,P^{-1}=Q$。比如
$\begin{aligned}f_n&=\sum_{i=0}^n{n\choose i}g_i\\g_n&=\sum_{i=0}^n(-1)^{n+i}{n\choose i}f_i\end{aligned}$
我们可以构造出列向量G,F,则有$PG=F,G=QF$,写开就是二项式反演的式子。
再比如斯特林反演:
$\sum \limits_{k=m}^n s_2(n,k)s_1(k,m)(-1)^{n+k}=[n=m]$
我们可以构造出矩阵和逆矩阵,同理就可以得到斯特林反演的式子:
$\begin{aligned}f(n)&=\sum_{i=1}^n \begin{Bmatrix}n\\i\end{Bmatrix}g(i)\\g(n)&=\sum_{i=0}^n(-1)^{n-i}\begin{bmatrix}n\\i\end{bmatrix}f(i)\end{aligned}$
证明斯特林反演的基本式子:
首先:
$x^{\underline n}=(-1)^n (-x)^{\overline n}$
$x^{\overline n}=(-1)^n (-x)^{\underline n}$
证明:
$\begin{aligned}n^m&=\sum_{i=0}^m\begin{Bmatrix}m\\i\end{Bmatrix}n^{\underline i}\\&=\sum_{i=0}^m\begin{Bmatrix}m\\i\end{Bmatrix}(-1)^i(-n)^{\overline i}\\&=\sum_{i=0}^m\begin{Bmatrix}m\\i\end{Bmatrix}(-1)^i\sum_{j=0}^i \begin{bmatrix}i\\j\end{bmatrix}(-n)^j\\&=\sum_{j=0}^m (-n)^j\sum_{i=j}^m\begin{Bmatrix}m\\i\end{Bmatrix}\begin{bmatrix}i\\j\end{bmatrix}(-1)^i\\&=\sum_{j=0}^m n^j\sum_{i=j}^m\begin{Bmatrix}m\\i\end{Bmatrix}\begin{bmatrix}i\\j\end{bmatrix}(-1)^{i+j}\end{aligned}$
显然当且仅当$j=m$时后面那堆式子才为1。
# 二项式反演
![](https://cdn.luogu.com.cn/upload/pic/48343.png)
![](https://cdn.luogu.com.cn/upload/pic/48344.png)
### 经典问题:错排
![](https://cdn.luogu.com.cn/upload/pic/48415.png)
### 经典问题2
求长度为n且仅包含小写英文字母且循环节长度恰为n的字符串的个数。
循环节……就是最短的复制若干遍后拼起来跟原串相等的字符串。
![](https://cdn.luogu.com.cn/upload/pic/48416.png)
----------------------
接下来是题目
## CF715E
比较难的题,我思考了一下午。
[yyb的Blog](https://www.cnblogs.com/cjyyb/p/10149652.html)
首先转化成数轮换的个数。然后我们要把边建出来,并把中间连续的缩点,变成四种边:已知-已知,已知-0,0-已知,0-0。(所以要注意,dfs记录的是起始点,不是上一点)
然后我们要计算用x-0或0-x边拼成环的方案数:
$g_x=\sum_{i=x}^k{k\choose i}\begin{bmatrix}i\\x\end{bmatrix}(m+k-i)^{\underline{k-i}}$
其中m是0−0边的数量。
理解:先决定环有多少x-0边构成,然后它们组成环的方案数是S1,剩下的每条x-0边可以随意选一条x-0边或0-0边作为后继,所以要乘上下降幂。
注意,这里的g不仅计算了组成环的方案数,对于没有组成环的x-0边,它们的方案数也被算进去了!手玩一下可以得到。
0-x边同理。
然后还有一类边:0-0边,它们组成环的方案数也是S1.
最后把三个数组卷积起来即可。
```cpp
#define N 4005
int cnt[4];
int C[N][N],A[N][N],S[N][N];
void calc(int f[],int n)
{
for(ri i=0; i<=n; ++i)
for(ri j=i; j<=n; ++j)
inc(f[i],mul(mul(C[n][j],S[j][i]),A[n-j+cnt[0]][n-j]));
for(ri i=0; i<=n; ++i)
for(ri j=i+1; j<=n; ++j)
if((i-j)&1) dec(f[i],mul(C[j][i],f[j]));
else inc(f[i],mul(C[j][i],f[j]));
// out(n);
// prt(f,n+1);
}
int n;
int a[N],b[N];
bool vis[N];
int e[N];
int deg[N];
void dfs(int x,int f)
{
vis[x]=1;
// printf("D %d %d\n",x,f);
if(e[x])
{
if(!vis[e[x]]) dfs(e[x],f);
else ++cnt[3];
}
else
{
if(f>n&&x>n) ++cnt[0];
else if(f>n&&x<=n) ++cnt[1];
else if(f<=n&&x>n) ++cnt[2];
}
}
int g[N],f[N],h[N];
int ans[N];
signed main()
{
#ifdef M207
freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
#endif
in(n);
for(ri i=1; i<=n; ++i) in(a[i]);
for(ri i=1; i<=n; ++i) in(b[i]);
for(ri i=1; i<=n+n; ++i) vis[i]=1;
for(ri i=1; i<=n; ++i)
{
if(!a[i]) a[i]=n+i;
if(!b[i]) b[i]=n+i;
vis[a[i]]=vis[b[i]]=0;
if(a[i]<=n||b[i]<=n) e[a[i]]=b[i],++deg[b[i]];
}
for(ri i=1; i<=n+n; ++i) if(!vis[i]&&!deg[i]) dfs(i,i);
for(ri i=1; i<=n+n; ++i) if(!vis[i]) dfs(i,i);
C[0][0]=A[0][0]=S[0][0]=1;
for(ri i=1; i<=n; ++i)
{
C[i][0]=A[i][0]=1;
for(ri j=1; j<=i; ++j)
{
C[i][j]=add(C[i-1][j-1],C[i-1][j]);
A[i][j]=mul(A[i][j-1],i-j+1);
S[i][j]=add(S[i-1][j-1],mul(S[i-1][j],i-1));
}
}
calc(f,cnt[1]); calc(g,cnt[2]);
for(ri i=0; i<=n; ++i)
for(ri j=0; j<=i; ++j)
inc(h[i],mul(f[j],g[i-j]));
for(ri i=0; i<=n; ++i)
for(ri j=0; j<=i; ++j)
inc(ans[i],mul(h[j],S[cnt[0]][i-j]));
for(ri i=0; i<=n; ++i) ans[i]=mul(ans[i],A[cnt[0]][cnt[0]]);
for(ri i=0; i<n; ++i) out(n-i-cnt[3]>=0?ans[n-i-cnt[3]]:0,' ');
return 0;
}
```