51Nod 1020 离线+DP

来源:互联网 发布:田园城市 知乎 编辑:程序博客网 时间:2024/05/22 00:38

题目链接


题意:
定义一个排列的逆序数为排列中逆序对的个数
给出nk,问n位数的所有排列中,逆序数为k的排列个数有多少。


思路:
考虑动态规划
定义dp[i][j]:i位数的排列中逆序数为j的排列个数

考虑对于n+1位数的排列,可以看作是(n+1)这个数插入到n位数的排列中而形成。

那么对于dp[n+1][k]的求取:
可以考虑利用(n+1)一定大于1n中的任意一个数的性质。

初始dp[n+1][k]=0
假设第(n+1)这个数插入到n位数的排列最后一位,且最后新的排列的逆序数为k
则:

dp[n+1][k]=dp[n+1][k]+dp[n][k]

若第(n+1)这个数插入到n位数的排列倒数第二位,因为(n+1)一定是最大的数,故他跟他后面的数均会对答案有新的贡献,且贡献等于他后面的数的个数:
则:

dp[n+1][k]=dp[n+1][k]+dp[n][k1]

……依此类推

dp[n+1][k]=i=max(0,kn)kdp[n][i]

其中求和式可以利用前缀和进行优化时间复杂度。
因多组数据,可以考虑离线处理,优化空间复杂度。
最后总复杂度为:O(nk+T)n,k为多组数据中的最大值。

代码:

#include<cstdio>#include<algorithm>using namespace std;const int mod = 1e9 + 7;const int A = 2e4 + 10;int dp[A],sum[A],Ans[A],Mx_n,Mx_k,q;class Qu{public:    int n,k,id;    bool operator<(const Qu& rhs) const{        if(n != rhs.n) return n < rhs.n;        return k < rhs.k;    }}Q[A];void solve(int n,int k){    dp[0] = 1;    int tot = 1;    for(int i=2 ;i<=n ;i++){        sum[0] = dp[0];        while(tot<=q && Q[tot].n == i && Q[tot].k == 0){                Ans[Q[tot].id] = 1;                tot++;        }        for(int j=1 ;j<=k ;j++) sum[j] = (sum[j-1] + dp[j])%mod;        for(int j=1 ;j<=k ;j++){            if(j-i+1 > 0) dp[j] = (sum[j] - sum[j-i])%mod;            else          dp[j] = sum[j];            if(dp[j]<0) dp[j] += mod;            while(tot<=q && Q[tot].n == i && Q[tot].k == j){                Ans[Q[tot].id] = dp[j];                tot++;            }        }    }    //printf("tot = %d\n",tot);    for(int i=1 ;i<=q ;i++){        printf("%d\n",Ans[i]);    }}int main(){    scanf("%d",&q);    Mx_n = Mx_k = 0;    for(int i=1 ;i<=q ;i++){        scanf("%d%d",&Q[i].n,&Q[i].k);        Q[i].id = i;        Mx_n = max(Mx_n,Q[i].n);        Mx_k = max(Mx_k,Q[i].k);    }    sort(Q+1,Q+1+q);    solve(Mx_n,Mx_k);    return 0;}
原创粉丝点击