题目
传送门 to BZOJ(可是已经凉透了)
题目概要
对于每个
i
∈
[
2
,
n
]
i\in[2,n]
i∈[2,n],求出有多少个
i
×
i
i\times i
i×i 的
01
01
01 矩阵满足:每一行每一列的和都是
2
2
2 。
数据范围与约定
n
⩽
1
0
7
n\leqslant 10^7
n⩽107 。
思路
将第 i i i 行都看做节点 r i r_i ri,第 i i i 列都看做节点 c i c_i ci,若 a i j = 1 a_{ij}=1 aij=1 则将 r i r_i ri 和 c i c_i ci 之间连一条无向边。可以发现我们就是做 二分图环剖分。事实上 o e i s \rm oeis oeis 已收录此数列。
无论如何,需要先求出
g
n
g_n
gn 为,大小为
2
n
2n
2n 的环有多少种。显然
X
\frak X
X 部和
Y
\frak Y
Y 部可以分别排列,避免重复而规定
X
\frak X
X 的某个元素为首,又考虑到环是不分方向的,可知
g
n
=
n
!
(
n
−
1
)
!
2
(
n
⩾
2
)
g_n=\frac{n!(n{-}1)!}{2}\quad(n\geqslant 2)
gn=2n!(n−1)!(n⩾2)
思路壹
枚举其中的一个环,要求必须包含某个特定点。记答案为
f
n
f_n
fn 则
f
n
=
∑
x
=
2
n
(
n
x
)
(
n
−
1
x
−
1
)
g
x
f
n
−
x
=
n
!
(
n
−
1
)
!
2
∑
x
=
2
n
x
!
(
x
−
1
)
!
x
!
[
(
n
−
x
)
!
]
2
(
x
−
1
)
!
f
n
−
x
⇒
f
n
(
n
!
)
2
=
1
2
n
∑
x
=
2
n
f
n
−
x
[
(
n
−
x
)
!
]
2
\begin{aligned} f_n &=\sum_{x=2}^{n}{n\choose{x}}{n{-}1\choose x{-}1}g_x f_{n-x}\\ &={n!(n{-}1)!\over 2}\sum_{x=2}^{n}\frac{x!(x{-}1)!}{x![(n{-}x)!]^2(x{-1})!}f_{n-x}\\ \Rightarrow\frac{f_n}{(n!)^2}&=\frac{1}{2n}\sum_{x=2}^{n}\frac{f_{n-x}}{[(n{-x})!]^2} \end{aligned}
fn⇒(n!)2fn=x=2∑n(xn)(x−1n−1)gxfn−x=2n!(n−1)!x=2∑nx![(n−x)!]2(x−1)!x!(x−1)!fn−x=2n1x=2∑n[(n−x)!]2fn−x
简单前缀和可做到 O ( n ) \mathcal O(n) O(n) 。
思路贰
有标号连通块划分?记
F
(
x
)
=
∑
n
⩾
2
n
!
(
n
−
1
)
!
2
⋅
x
n
(
n
!
)
2
=
∑
n
⩾
2
x
n
2
n
F(x)=\sum_{n\geqslant 2}\frac{n!(n{-}1)!}{2}\cdot\frac{x^n}{(n!)^2}=\sum_{n\geqslant 2}\frac{x^n}{2n}
F(x)=∑n⩾22n!(n−1)!⋅(n!)2xn=∑n⩾22nxn,则只需求出
G
(
x
)
=
exp
[
F
(
x
)
]
G(x)=\exp[F(x)]
G(x)=exp[F(x)]
注意这里的 F ( x ) F(x) F(x) 不是常见的指数型生成函数的形式;因为它的组合数系数实际上是 ( n x ) 2 {n\choose x}^2 (xn)2,所以阶乘需平方。
这是可以求解的。只需注意到
F
′
(
x
)
=
∑
n
⩾
2
x
n
−
1
2
=
x
2
−
2
x
F'(x)=\sum_{n\geqslant 2}\frac{x^{n-1}}{2}=\frac{x}{2-2x}
F′(x)=n⩾2∑2xn−1=2−2xx
而后有
G
′
(
x
)
=
G
(
x
)
F
′
(
x
)
⇒
(
2
−
2
x
)
⋅
G
′
(
x
)
=
x
G
(
x
)
G'(x)=G(x)F'(x)\Rightarrow(2-2x)\cdot G'(x)=xG(x)
G′(x)=G(x)F′(x)⇒(2−2x)⋅G′(x)=xG(x)
设
G
(
x
)
=
∑
n
⩾
0
f
n
x
n
G(x)=\sum_{n\geqslant 0}f_nx^n
G(x)=∑n⩾0fnxn,对比系数有
2
(
n
+
1
)
f
n
+
1
−
2
n
f
n
=
f
n
−
1
2(n{+}1)f_{n+1}-2nf_n=f_{n-1}
2(n+1)fn+1−2nfn=fn−1
整理后可以 O ( n ) \mathcal O(n) O(n) 递推。
代码
此处给出 思路壹
的代码。
#include <cstdio>
const int mod = 998244353;
const int MAXN = 10000005;
int n, inv[MAXN];
int main () {
scanf("%d", &n);
inv[0] = inv[1] = 1;
for(int i = 2; i <= n; ++i) // 逆元
inv[i] = 1ll * (mod - mod/i) * inv[mod%i] % mod;
int fac = 1, sumF = 1, F, ans = 0, lstF = 0;
// lstF = F_{i-1}, F = F_{i}, sumF = \sum_{i=0}^{n-2} F_i
for(int i = 2; i <= n; ++i) {
fac = 1ll * fac * i % mod; // fac = i!
F = 1ll * sumF * inv[i] % mod * inv[2] % mod;
ans = (ans + 1ll * F * fac % mod * fac % mod) % mod;
sumF = (sumF + lstF) % mod;
lstF = F;// F_{i-1}
}
printf("%d\n", ans); // ans = \sum_{i=1}^{n} f_i
}
代码源自 Ark \textsf{Ark} Ark 。这个链接只是个人主页罢了。