Loading

【NOIP2015模拟11.5】JZOJ8月5日提高组T1 俄罗斯套娃

【NOIP2015模拟11.5】JZOJ8月5日提高组T1 俄罗斯套娃

题目


题解

题意就是说
将1~\(n\)排列,问有多少种方案使得序列的逆序对个数小于\(k\)
很容易想到DP
\(f[i][j]\)表示当前到了插入第\(i\)个数,有\(j\)个逆序对的排列方案数
转移显然
\(f[i][j]=\sum_{k=j-i+1}^jf[i-1][k]\)
显而易见,这个转移是\(O(n^2k)\)的,会只有\(60\%\)
那么思考优化
既然是求和,为什么不用前缀和呢
所以可以用一个数组来记录前缀和,时间优化至\(O(nk)\)
但是注意到空间

手算一下:\(3000*3000*8/1024≈70312\)
所以说,要开滚动
注意取模

#include<cstdio>
#include<iostream>
using namespace std;
long long n,k,i,j,u,one,two,x,s,mod,f[2][3005],sum[3005];
int main()
{
    freopen("matryoshka.in","r",stdin);
    freopen("matryoshka.out","w",stdout);
    mod=10000000007;
    scanf("%lld%lld",&n,&k);
    x=1;
    for (i=1;i<=n;i++)
        x=x*i%mod;
    s=n*(n-1)/2;
    if (k>=s) 
    {
        printf("%lld\n",x);
        fclose(stdin);
        fclose(stdout);
        return 0;
    }
    for (j=0;j<=k;j++)
        sum[j]=1;
    one=0;
    two=1;
    f[0][0]=1;
    for (i=1;i<=n;i++)
    {
        swap(one,two);
        for (j=0;j<=k;j++)
            if (j>=i) f[one][j]=(sum[j]-sum[j-i]+mod)%mod;
            else f[one][j]=sum[j]%mod;
        for (j=0;j<=k;j++)
            sum[j]=f[two][j]=0;
        sum[0]=f[one][0];
        for (j=1;j<=k;j++)
            sum[j]=(sum[j-1]+f[one][j])%mod;
    }
    printf("%lld\n",sum[k]);
    fclose(stdin);
    fclose(stdout);
    return 0;
}
posted @ 2020-08-05 15:04  Thunder_S  阅读(131)  评论(0编辑  收藏  举报