Dp搬运工3

mengbier学长的一道好题。看了提示才做出来。

首先发现的((a_i,b_i))的顺序对答案没有影响。考虑把b序列从1到n固定,依次把a序列的(1..n)分配给b。假如当前扫到了(i),分情况讨论分在左边还是右边,我们可以把分在右边的数看成是“游离的”,也就是位置可以随便换,需要的时候再取出来。

大体思路就是这样,剩下的就靠你了。

code

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
#define forg(i,x) for(int i=first[x];i;i=nxt[i])
#define uu unsigned
#define fi first
#define se second
#define od(x) ((x)&1)
#define ev(x) (od(x)^1)
#define mi2(x) (1<<(x))
#define scanf a1234=scanf
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
int a1234;
char buf[1<<18],*bufs=buf,*buft=buf;
inline int gc(){
    return bufs==buft&&(buft=(bufs=buf)+fread(buf,1,1<<18,stdin)),bufs==buft?-1:*bufs++;
}
inline void xxx(){for(;;);}

const int mod=998244353;
int dp[51][51][2501],b[11],n,K;
inline bool vad(int i,int j){return i>=j&&i-j<=n-i;}
inline void pr(){
    for(int i=1;i<=n;++i)for(int j=0;j<=i;++j)for(int k=0;k<=i*i;++k)if(dp[i][j][k])printf("%d %d %d %d
",i,j,k,dp[i][j][k]);
}
inline void pr1(){
    int i=n;
    for(int j=0;j<=i;++j)for(int k=0;k<=i*i;++k)if(dp[i][j][k])printf("%d %d %d
",j,k,dp[i][j][k]);
}
inline void sum(int &a,int b){a+=b;if(a>=mod)a-=mod;}
#define pr pr1
int main(){fre(D);//cout<<(sizeof(dp)>>20)<<endl;
    scanf("%d%d",&n,&K);
    dp[0][0][0]=1;
    for(int i=0;i<n;++i)for(int j=0;j<=i;++j)for(int k=0;k<=i*i;++k)if(dp[i][j][k]){
        if(vad(i+1,j+1))sum(dp[i+1][j+1][k+i+1], 1ll*dp[i][j][k]*(i-j+1)%mod);
        if(vad(i+1,j))  sum(dp[i+1][j][k],dp[i][j][k]);
        if(j!=i){
            if(vad(i+1,j+1))sum(dp[i+1][j+1][k+i+1],1ll*dp[i][j][k]*(i-j)%mod);
            if(vad(i+1,j+2))sum(dp[i+1][j+2][k+(i+1)*2],1ll*dp[i][j][k]*(i-j)*(i-j)%mod);
        }
    }
    long long ans=0;
    for(int i=K;i<=n*n;++i)ans+=dp[n][n][i];
    ans%=mod;
    for(int i=1;i<=n;++i)ans*=i,ans%=mod;
    printf("%lld
",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/happyguy/p/13818797.html