P r o b l e m \mathrm{Problem} Problem
S o l u t i o n \mathrm{Solution} Solution
我们的想法就是求出每一个长度序列的答案.
这类排列问题我们可以考虑用递推的方法来解决.
假如我们已经知道了长度为 i i i- 1 1 1序列的答案,现在我们想要知道长度为 i i i的答案.
我们会发现,我们相当于是在 [ 1 , i − 1 ] [1,i-1] [1,i−1]的排列中添加一个数字 i i i.
那么如果数字 i i i添加的要有贡献,只有选择添加在最开头才行.
现在我们考虑贡献的计算:
- 显然,我们如果不添加的话至少需要保留原来的贡献.
- 原来长度为 i − 1 i-1 i−1的序列有i个位置可以添加,保留原序列的贡献为 f i − 1 ∗ i f_{i-1}*i fi−1∗i.
- 现在考虑新增的贡献,显然对于某一个原来的数列答案的贡献为
a
a
a,那么添加在开头贡献就是
a
+
1
a+1
a+1,所以贡献的增加我们可以最差来考虑:
△ v a l = ( a + 1 ) 2 − a = 2 a + 1 △val=(a+1)^2-a=2a+1 △val=(a+1)2−a=2a+1 - 我们先思考一下 2 a + 1 2a+1 2a+1中的 1 1 1,每一个长度为 i − 1 i-1 i−1的数列都可以增加1的贡献,则贡献是 ( i − 1 ) ! (i-1)! (i−1)!.
- 对于其中的 2 a 2a 2a来说,相当于不带平方的贡献,我们假设为 g i g_i gi.
- 那么我们就有: f i = f i − 1 × i + 2 g i − 1 + ( i − 1 ) ! f_i=f_{i-1}\times i+2g_{i-1}+(i-1)! fi=fi−1×i+2gi−1+(i−1)!
接下来考虑 g i g_i gi的计算:
- 显然贡献在于开头,不添加的贡献为: g i − 1 × i g_{i-1}\times i gi−1×i.
- 添加的贡献为 ( i − 1 ) ! (i-1)! (i−1)!.
- 所以就有: g i = g i − 1 × i + ( i − 1 ) ! g_i=g_{i-1}\times i+(i-1)! gi=gi−1×i+(i−1)!.
C o d e \mathrm{Code} Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 6e5;
const int P = 998244353;
int T, n;
int fac[N], f[N], g[N];
int read(void) {
int s = 0, w = 0;
char c = getchar();
while (c < '0' || c > '9') w |= c == '-', c = getchar();
while (c >= '0' && c <= '9') s = s * 10 + c - 48, c = getchar();
return w ? -s : s;
}
signed main(void)
{
freopen("mini.in","r",stdin);
freopen("mini.out","w",stdout);
T = read();
fac[0] = f[1] = g[1] = 1;
for (int i=1;i<=1e5;++i)
fac[i] = fac[i-1] * i % P;
for (int i=2;i<=1e5;++i) {
g[i] = (g[i-1] * i + fac[i-1]) % P;
f[i] = (f[i-1] * i + g[i-1] * 2 + fac[i-1]) % P;
}
while (T --) printf("%lld\n", f[read()]);
return 0;
}