Matrix
题意:一个
n
×
n
n\times n
n×n 的矩阵,每个位置放置一个数,数的范围
∈
[
1
,
n
2
]
\in [1,n^2]
∈[1,n2],从第
1
1
1 行到第
n
n
n 行,每行的最小值形成一个集合
A
=
A=
A= {
a
1
,
a
2
,
a
3
.
.
.
a
n
a_1,a_2,a_3...a_n
a1,a2,a3...an},与集合
B
=
B=
B= {
1
,
2
,
3
,
.
.
.
.
n
1,2,3,....n
1,2,3,....n} 的交集生成集合
S
S
S,
∣
S
∣
|S|
∣S∣ 表示集合
S
S
S 所有数之和,求所有的
S
S
S 集合的
∣
S
∣
|S|
∣S∣ 之和。
思路:
每一个矩阵可以生成一个集合
A
=
A=
A= {
a
1
,
a
2
,
a
3
.
.
.
a
n
a_1,a_2,a_3...a_n
a1,a2,a3...an},与集合
B
B
B 取交集进而生成一个集合
S
S
S。
我们考虑计算贡献
先看
1
1
1,无论它在矩阵的哪个位置始终会生成到集合
A
A
A,然后看
2
2
2,它只有与
1
1
1 在同一行的时候才无法产生贡献,看
3
3
3,只有
1
,
2
1,2
1,2 的位置会对
3
3
3 产生贡献有影响,然后我们可以发现,对于
i
∈
(
1
,
2
,
3
,
.
.
.
n
)
i \in (1,2,3,...n)
i∈(1,2,3,...n),只有
<
i
<i
<i 的
i
−
1
i-1
i−1个数的位置会对
i
i
i 产生的贡献有影响。
一开始我考虑用所有的情况
−
-
− 无法产生贡献的情况去计算答案(
n
=
2
n=2
n=2的时候过样例了以为写对了,结果一直wa,写的代码比较烂,有时候还T),然后发现情况比较复杂,如果考虑直接计算产生贡献的情况其实是不复杂的。
我们先选定
i
i
i ,然后给在
n
2
−
i
n^2-i
n2−i 个 数中选
n
−
1
n-1
n−1 个数来和它在一排(
C
n
2
−
i
n
−
1
C_{n^2-i}^{n-1}
Cn2−in−1),然后乘以这一行的全排列
A
n
n
A_n^n
Ann,然后乘以
C
n
1
C_n^1
Cn1,确定这行在哪,然后再安排剩下的
n
2
−
n
n^2-n
n2−n 个数。
首先选出
n
n
n 个数,
C
n
2
−
n
n
C_{n^2-n} ^ n
Cn2−nn 在一行,然后全排列
然后继续选
n
n
n 个数,
C
n
2
−
2
n
n
C_{n^2-2n} ^n
Cn2−2nn 在一行,然后全排列
…
然后我们把它写成分数的形式就可以发现可以化简
n
!
(
n
2
−
n
)
!
n
!
(
n
2
−
2
n
)
!
×
n! \frac{(n^2-n)!}{n!(n^2-2n)!} \times
n!n!(n2−2n)!(n2−n)!×
n
!
(
n
2
−
2
n
)
!
n
!
(
n
2
−
3
n
)
!
×
n! \frac{(n^2-2n)!}{n!(n^2-3n)!} \times
n!n!(n2−3n)!(n2−2n)!×
n
!
(
n
2
−
3
n
)
!
n
!
(
n
2
−
4
n
)
!
×
n! \frac{(n^2-3n)!}{n!(n^2-4n)!} \times
n!n!(n2−4n)!(n2−3n)!× …
n
!
2
n
!
n
!
n
!
×
n! \frac{2n!}{n!n!} \times
n!n!n!2n!×
n
!
n
!
n
!
0
!
n! \frac{n!}{n!0!}
n!n!0!n!
然后我们可以发现,每一项分子分母都有
n
!
n!
n!,就都约掉了,分母的一部分可以和后一项分子约掉,最后剩下
(
n
2
−
n
)
!
{(n^2-n)!}
(n2−n)!
举个例子简化这个过程:比如有
1
,
2
,
3
,
4
,
5
1,2,3,4,5
1,2,3,4,5 这
5
5
5 个数,求全排列,其中
3
3
3 是特殊的,我们先安排3的位置,有
5
5
5个位置可以选择,然后考虑其他的数
C
4
1
C_4^1
C41,再选一个
C
3
1
C_3^1
C31,继续
C
2
1
C_2^1
C21,
C
1
1
C_1^1
C11,最后得到
A
5
5
A_5^5
A55。
所以我们得到公式
∑
i
=
1
i
=
n
C
n
2
−
i
n
−
1
A
n
n
C
n
1
(
n
2
−
n
)
!
\sum_{i=1}^{i=n}C_{n^2-i}^{n-1}A_n^nC_n^1(n^2-n)!
i=1∑i=nCn2−in−1AnnCn1(n2−n)!
(其实好像也可以这么理解,安排第一行和它的位置之后,剩下的数进行全排列。
我们预处理一下所有的阶乘,注意计算组合数的时候就没必要用
f
o
r
for
for 来计算,直接用阶乘+逆元求得,不然也会
T
L
E
TLE
TLE。
复杂度:
O
(
n
l
o
g
(
n
)
)
O(nlog(n))
O(nlog(n))
code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 5e3 + 9;
const int maxm = 2e7 + 5e6 + 9;
const int mod = 998244353;
ll A[maxm];
ll n;
ll q_pow(ll a, ll b){
ll ans = 1;while(b){
if(b & 1) ans=(ans*a)%mod;a=(a*a)%mod;b>>=1;
}return ans;
}
ll zh(ll n, ll m)
{
ll ans1 = A[n] * q_pow(A[n-m],mod-2) % mod;
return ans1 * q_pow(A[m], mod-2ll) % mod;
}
void work()
{
scanf("%lld", &n);
ll ans = 0;
ll tmp = n * n;
ll tt = A[tmp-n] * n % mod;// 观察可以发现这项可以提出来最后算
for(ll i = 1; i <= n; ++i)
{
ll x = zh(tmp-i, n-1) * A[n] % mod;
ans = (ans + x % mod) % mod;
}
printf("%lld\n", ans*tt%mod);
}
int main()
{
A[0] = 1;
for(ll i = 1; i <= maxm - 9; ++i) A[i] = A[i-1] * i % mod;
int TT;scanf("%d",&TT);while(TT--)
work();
return 0;
}