DP+树状数组优化-sort it-neu2016第一次月赛

来源:互联网 发布:网络时间参数计算例题 编辑:程序博客网 时间:2024/06/05 08:11

问题 F: Sort It

时间限制: 6 Sec  内存限制: 128 MB
提交: 31  解决: 9
[提交][状态][讨论版]

题目描述

You are given a permutation of length n:p1,p2,...pn.Consider arrays of length n with possibly equal number from 1 to n.

If after rearrange elements of the array in the order of “all numbers equal to p1,all numbers equal to p2...all numbers equal to pn”,

the array become non-decreasing,then we call the array is sortable by p.

Calculate the total number of arrays that are sortable by p.

As the answer can be very large,output it after module 1e9+7.

输入

Several test cases.

The first line contains a single integer n(1<=n<=2000),the length of permutation.

The second line contains n distinct integers p1,p2,..pn(1<=pi<=n).

输出

Output a singe number -- the answer to the problem module 1e9+7.

样例输入

22 132 1 3

样例输出

215

提示

[提交][状态][讨论版]


题目大意:有点难懂。。大致就是给一个n,然后再给一个n个数的排列,问所有(1,...,1)到(n,....,n)的排列中如果通过一个操作,将所有的数按照先前给出的那个数列的顺序进行排列,会形成一个不下降序列,求这样的排列个数。
题目解法:读懂题意后,很容易想到求出所有不同长度的不下降序列的个数,然后分别对应乘上一个事先可以求出的x种数形成n的排列的个数,最后将所有结果相加就好。x种数形成n的排列的个数可以用递推求。递推式是f[i][j]=(f[i-1][j-1]*j%mod+f[i-1][j]*j%mod)%mod;初始f[0][0]=1;之后就是一个求不下降序列的长度问题,很容易想到一个n三次方的DP方法但是时间复杂度不够,我们需要通过树状数组优化到n方logn。具体优化是将三方算法中的求和公式dp[i][j]=∑dp[k][j-1]时,a[k]<a[i]时,实现对原数组进行排序,然后用树状数组直接求比他小的dp值。
具体代码:
<span style="font-style: normal;">#include<bits/stdc++.h>using namespace std;const long long mod=1e9+7;int n;long long dp[2222][2222];     //dp[i][j]表示以下表i结尾,长度为j的不下降子序列。其中i是新的i(排序后的)long long tree[2222][2222];     //树状数组维护当前的值struct NODE {    long long num;    int pos;}a[2222];int lowbit(int x) {    return x&-x;}void add(int x,int l,int v) {    // l 是指长度其实是个一维树状数组的叠加    for(int i=x;i<=n;i+=lowbit(i)) tree[i][l]=(tree[i][l]+v)%mod;}long long query(int x,int l) {    long long sum=0;    for(int i=x;i>0;i-=lowbit(i)) {        sum=(sum+tree[i][l])%mod;    }    return sum;}bool cmp(NODE a,NODE b) {    return a.num<b.num;}long long f[2222][2222];int main(){    f[0][0]=1;    for(int j=1;j<=2000;j++)             //初始x种元素组合成n个数的排列        for(int i=j;i<=2000;i++) {            f[i][j]=(f[i-1][j-1]*j%mod+f[i-1][j]*j%mod)%mod;        }    while(scanf("%d",&n)!=EOF) {        memset(dp,0,sizeof dp);        memset(tree,0,sizeof tree);        for(int i=1;i<=n;i++) {            scanf("%lld",&a[i].num);    //记录原先坐标            a[i].pos=i;        }        sort(a+1,a+n+1,cmp);        for(int i=1;i<=n;i++) {            for(int j=1;j<=i;j++) {                if(j==1) dp[i][j]=1;                else dp[i][j]=query(a[i].pos-1,j-1);  //询问比他小的数所以pos-1,询问少一个数的所以j-1。                add(a[i].pos,j,dp[i][j]);    //修改            }        }        long long ans=0;        for(int i=1;i<=n;i++)            for(int j=1;j<=i;j++)                ans=(ans+dp[i][j]*f[n][j])%mod;        printf("%lld\n",ans);    }    return 0;}</span>



0 0