题目
题解
一开始想的是dp,但是需要(dp[i][j])代表在(n imes i)的方格中放入(j)个数使得每列都至少有一个数的方案数,这样答案就是
[n!(n^2-n)!sumlimits_{i=1}^{n}{icdot dp[i][n] cdot C(n,i)}
]
但是计算dp复杂度是(O(n^3)),不可做。
突破点是分别计算1~n每个数的贡献。对于数(i),当它所在行的其他数都大于(i)时对答案有1的贡献,这样的局面有(C(n^2-i,n-1) cdot n),乘(n)是(i)在一行有(n)个位置可以选。此时(i)的总贡献为(C(n^2-i,n-1) cdot n)。再加上全排列,答案为
[n!(n^2-n)!sumlimits_{i=1}^{n}{C(n^2-i,n-1) cdot n}
]
将外面(n!(n^2-n)!)移进去可以消掉很多项,这样就不会T。
#include <bits/stdc++.h>
#define endl '
'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define mp make_pair
#define seteps(N) fixed << setprecision(N)
typedef long long ll;
using namespace std;
/*-----------------------------------------------------------------*/
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f
const int N = 2.5e7 + 10;
const int M = 998244353;
const double eps = 1e-5;
ll fact[N], rfact[N], inv[N];
inline ll qpow(ll a, ll b, ll m) {
ll res = 1;
while(b) {
if(b & 1) res = (res * a) % m;
a = (a * a) % m;
b = b >> 1;
}
return res;
}
int main() {
IOS;
fact[0] = 1;
for(int i = 1; i < N; i++) {
fact[i] = fact[i - 1] * i % M;
}
int t;
cin >> t;
while(t--) {
int n;
cin >> n;
ll ans = 0;
ll tn = n * n - n;
ll pw = 1;
for(int i = 1; i <= n; i++) {
ans = (ans + fact[n * n - i] * n % M * n % M * pw % M) % M;
pw = pw * tn % M;
tn--;
}
cout << ans << endl;
}
}