T3
题解
10pts
暴力 dfs。
20pts
状压 dp。状态为当前选了的数,保存这样的方案数和逆序对总数枚举下一个数推到下一个状态,转移方程不难推,反正是部分分不展开讲,可能需要打表防止
40~80pts
这几个点意义不明 我不知道有什么方法卡过这几个点。
100pts
首先我们需要学会求长度为
设
首先,很显然
另外特别注意一下
对于
假如将
假如
由于除了当前数外每个数都可能成为
下面我们进行逆序对的计算。
考虑在长度为
我们进行分类讨论:
-
的情况。容易发现这种情况可以两两配对形成双射(只要交换 就可以了),并且配对的其中一个必定造成一点贡献,另一个必定不会(显然 在 左边会造成贡献,反过来就不会)。另外值得一提的是,此时我们统计的是 两个值产生的贡献,因为 两个位置上的贡献会在计算其他数对的时候被统计。为方便叙述,我们称满足这种情况的错排构成的集合为 。 -
或 的情况,此时我们令不等的那个为1 ,此时统计的是 两个位置上产生的贡献。- 若 $kj
p_i=k,p_j=i p_i=j,p_j=k i'=k,j'=i B$。 - 若
,我们仍可以像上面一样构成双射,但需要注意,此时两边都会有贡献,这个我们后面再讲,我们称这种情况的集合为 。
- 若 $kj
的情况,这种情况并不能构成双射,但是这样显然会构成逆序对,并且它十分好统计,我们参考之前算错排个数的思想,容易推出这种情况的个数为 ,我们称这种情况的构成的集合为 。
容易发现,
但是这并不正确,我们之前提到了
考虑有多少种数对会组成
接下来我们考虑
先考虑
综上所述,预处理
我们可以进行一些简单的化简,容易发现
code
code
#include<iostream>
#include<algorithm>
#include<string.h>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#include<vector>
#define pbk(a) emplace_back(a)
#define forup(i,s,e) for(register ll i=(s);i<=(e);i++)
#define fordown(i,s,e) for(register ll i=(s);i>=(e);i--)
#define abs(x) (((x)^((x)>>63))-((x)>>63))
using namespace std;
#define gc getchar()
inline ll read(){
ll x=0,f=1;char c;
while(!isdigit(c=gc)) if(c=='-') f=-1;
while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=gc;}
return x*f;
}
#undef gc
const ll N=1e7+5,inf=0x3f3f3f3f,mod=998244353;
ll t,n;
ll f[N];
ll inv2,inv6;
ll ksm(ll a,ll b){
ll c=1;
while(b){
if(b&1) c=c*a%mod;
a=a*a%mod;
b>>=1;
}
return c;
}
signed main(){
inv2=ksm(2,mod-2);inv6=ksm(6,mod-2);//预处理 2 和 6 的逆元
f[2]=1;f[3]=2;f[0]=1;
forup(i,4,1e7){
f[i]=(f[i-2]*(i-1)%mod+f[i-1]*(i-1)%mod)%mod;
}
t=read();
while(t--){
n=read();
if(n==1){puts("0");continue;}
ll shuangshe=(f[n]+f[n-2])*inv2%mod*n%mod*(n-1)%mod*inv2%mod;
ll trible=n*(n-1)%mod*inv6%mod;
ll add=(trible*f[n-1]%mod)%mod;
printf("%lld\n",(shuangshe+add)%mod);
}
}