\(\mathcal{Description}\)

多组询问
\(1 \leq n,Q\leq 10^5\)

\(\mathcal{Solution}\)

\(50\)分解法

考虑\(DP\),感觉上直接算答案不好算,所以考虑算长度为\(n\)的所有排列改变的次数的排列的个数算出来
之后再把个数乘以次数的平方即可
\(f_{ij}\)表示长度为\(i\)的排列的改变次数\(j\)排列个数
考虑把最大的\(i\)插入到长度为\(i-1\)的改变次数为\(j\)的排列中,则有\(i\)个空可以插,其中最前面的空插进去会使改变次数加一,所以有\(i-1\)个空使改变次数不变
考虑插在最前面则原改变次数应为\(j-1\)
所以\(f_{ij}=f_{i- 1j}*(i-1)+f_{i-1j-1}\)

提前预处理一下即可做到 \(n^{2}\)

\(100\)分解法

仍然是\(DP\),原来的是把问题拆开,好做但是不能直接算
不妨大胆一点,设\(f_i\)表示长度为\(i\)答案
仍然考虑从\(i-1\)转移过来
还是上面那句
考虑把最大的\(i\)插入到长度为\(i-1\)的改变次数为\(j\)的排列中,则有\(i\)个空可以插,其中最前面的空插进去会使改变次数加一,所以有\(i-1\)个空使改变次数不变
\(f_i=f_{i-1}*(i-1)+插到最前面的贡献\)
考虑插在最前面,原本的改变次数为\(x\)的都变为\(x+1\)
也就是\(x^2 -> (x+1)^2=x^2+2x+1\)
我们设\(g_i\)表示原本算贡献时是按照\(x\)来算的长度为\(i\)的答案
对于那个\(1\),因为插在最前面后面的\(i-1\)个数共有\((i-1)!\)种组合
所以\(f_i=f_{i-1}*(i-1)+f_{i-1}+2g_{i-1}+(i-1)!=f_{i-1}*i+f_{i-1}+(i-1)!\)
\(g_i\)同样这么推
\(g_i=g_{i-1}*i+(i-1)!\)

\(\mathcal{Code}\)

/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年09月28日 星期六 09时34分38秒
*******************************/
#include <cstdio>
#include <fstream>
using namespace std;
const int maxn = 100005;
const int mod = 998244353;
//{{{cin
struct IO{
    template<typename T>
    IO & operator>>(T&res){
        res=0;
        bool flag=false;
        char ch;
        while((ch=getchar())>'9'||ch<'0')   flag|=ch=='-';
        while(ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch^'0'),ch=getchar();
        if (flag)   res=~res+1;
        return *this;
    }
}cin;
//}}}
int T,n;
int f[maxn],g[maxn],fac[maxn];
int main()
{
    fac[0]=1;
    for (int i=1;i<=100000;++i){
        fac[i]=1ll*fac[i-1]*i%mod;
        f[i]=((1ll*f[i-1]*i%mod+2ll*g[i-1]%mod)%mod+fac[i-1])%mod;
        g[i]=(1ll*g[i-1]*i%mod+fac[i-1])%mod;
    }
    cin>>T;
    while (T--){
        cin>>n;
        printf("%d\n",f[n]);
    }
    return 0;
}
02-12 20:09