CCPC-Wannafly Winter Camp Day2 (Div2, onsite)

F:

/*
考虑每次选key,key=1...n的概率一样
所以下一层怎么分割这n个数的概率都相等
因此答案只和nk有关系
f[n][k]长度为n的序列 k是层数 逆序对期望个数*n! 
然后 分割之后的子部分离散化之后就变成子问题
所以方程 f[i][j]=sum( f[k-1][j-1]/(k-1)!*(j-1)!+f[i-k][j-1]/(i-k)!*(j-1)! )
然后我们把(j-1)!提出来 就可用前缀和优化
n*n 
*/
#include<cstdio>
#include<cstring>
#include<iostream>
#define maxn 6010
#define mod 998244353
#define ll long long
using namespace std;
ll T,n,k,f[maxn][maxn],cas,J[maxn],inv[maxn],sum[maxn];
ll qc(ll a,ll b){
    ll res=1;
    while(b){
        if(b&1)res=res*a%mod;
        a=a*a%mod;b>>=1;
    }
    return res;
}
void pre(){
    ll n=6000,k=6000,ni=qc(4,mod-2);
    J[1]=1;for(ll i=2;i<=n;i++)J[i]=J[i-1]*i%mod;
    for(ll i=1;i<=n;i++)inv[i]=qc(J[i],mod-2);
    for(ll i=2;i<=n;i++){
        f[i][1]=i*(i-1)*ni%mod*J[i]%mod;
        for(ll j=2;j<=i;j++)
            f[i][j]=(f[i][j]+sum[j-1]*J[i-1]%mod*2)%mod;
        for(ll j=1;j<=i;j++)
            sum[j]=(sum[j]+f[i][j]*inv[i]%mod)%mod;
    }
}
int main(){
    pre();scanf("%lld",&T);
    while(T--){
        scanf("%lld%lld",&n,&k);
        printf("Case #%lld: %lld\n",++cas,f[n][k]);
    }
    return 0;
}

 

posted @ 2019-02-28 22:21  一入OI深似海  阅读(110)  评论(0编辑  收藏  举报