# 牛客挑戰賽46_C題排列(字首優化DP)
阿新 • • 發佈:2020-12-30
牛客挑戰賽46_C題排列(字首優化DP)
題意:給定超級逆序對為k,求長度為n的排列的方案數;
超級逆序對:滿足 i<j 且 a[i]>a[j]+1 的二元組 (i,j);
題解:若題目是逆序對個數為k,對於從1到n的每一個數,從小至大的插入陣列,都有(0,i-1)的安排方案,列舉每一個數,記憶化搜尋一下即可,現在多了一個超級逆序對,即每個數最多貢獻i-2的超級逆序對的個數,並且會因為不同的i-1的位置影響不同的轉移。所以我們可以給dp加一維i-1的位置資訊,並加一層迴圈即可。空間是n3,時間是n4。爆空間,且超時。空間考慮滾動陣列優化,並且我們容易發現轉移具有單調性,所以利用預處理字首和優化dp,達到n2空間,n
#include<iostream> #include<cstring> using namespace std; #define ll long long const ll mod=998244353; ll n,k,lin[507][507],dp[507][507],pre[507][507]; ll pow(ll x,ll n,ll mod){ ll res=1; while(n>0){ if(n%2==1){ res=res*x; res=res%mod; } x=x*x; x=x%mod; n>>=1; } return res; } ll inv(ll x){ return pow(x,mod-2,mod); } int main(){ cin>>n>>k; lin[0][1]=1; for(int i=2;i<=n;i++){ for(int j=0;j<=k;j++){ for(int c=1;c<i;c++){ pre[j][c]=(pre[j][c-1]+lin[j][c])%mod; } } memset(dp,0,sizeof(dp)); for(int j=0;j<=k;j++){ for(int d=1;d<=i;d++){ dp[j][d]=(dp[j][d]+pre[j-i+d][d-1])%mod; dp[j][d]=(dp[j][d]+(pre[j-i+d+1][i-1]-pre[j-i+d+1][d-1]+mod)%mod)%mod; } } for(int j=0;j<=k;j++){ for(int d=1;d<=i;d++){ lin[j][d]=dp[j][d]; } } } ll ans=0; for(int i=1;i<=n;i++){ ans+=lin[k][i]; ans%=mod; } printf("%lld\n",inv(ans)); }