HDU 5542 ccpc 树状数组优化dp +离散化


给出长度为n的序列,问这个序列中有多少个长度为m的单调递增子序列。
dp[i][j],表示到第i个数字,长度为j的单调递增子序列的个数。需要注意的是取第j个数字

思路:通过枚举第i个数字来找出第i个数字前面存在的小于他的dp[k][i-1] 的数量  (i,j的位置不要颠倒了)


超时算法:

#include 
#define mod 1000000007 
using namespace std;

int dp[1010][1010];
int a[1010];
int main()
{
    int n,t;
    scanf("%d",&t);
    for(int cas=1;cas<=t;cas++)
    {
        int m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        memset(dp,0,sizeof(dp));
        
        for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
        dp[i][j]=0;
        
        dp[0][0]=1;
        for(int i=1;i<=m;i++)
        {
            for(int j=i;j<=n-m+2;j++)
            {
                for(int k=i-1;ka[k])
                    dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;
                }
            }
        }
        int sum=0;
        for(int i=1;i<=n;i++)
        {
            sum=(sum+dp[m][i])%mod;
        }
        printf("Case #%d: %d\n",cas,sum);
    }
    return 0;
}
树状数组将n^3优化成n^2logn

#include
#define INF 0x3f3f3f3f
#define bug puts("********************")
#define LL long long
#define mod 1000000007
using namespace std;

LL dp[1100][1011];
int a[1010],b[1010];
int n,m;
int init(int x)
{
    return x&(-x);
}
void update(int x,int y, int val)
{

    while(x<=n)
    {
        dp[x][y]=(dp[x][y]+val)%mod;
        x+=init(x);
    }
}
LL  getsum(int x,int y)
{
    LL sum=0;
    while(x>=1)
    {
        sum=(sum+dp[x][y])%mod;
        x-=init(x);
    }
    return sum;
}
int main()
{
    int t;
    scanf("%d",&t);
    for(int cas=1; cas<=t; cas++)
    {
        scanf("%d%d",&n,&m);
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
            b[i]=a[i];
        }
        sort(a+1,a+n+1);
        for(int i=1;i<=n;i++)
        {                                                             ///如: 5,3,1,可以离散化处理之后为 3,2,1
            b[i]=lower_bound(a+1,a+n+1,b[i])-(a);  ///  离散化,将一个值映射到一个在集合中大小相对位置不变的较小的值
        }

        memset(dp,0,sizeof(dp));
        LL sum=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=min(i,m);j++)
            {//bug;
                if(j==1)
                   update(b[i],1,1);
                else
                {
                    sum=getsum(b[i]-1,j-1);
                   update(b[i],j,sum);
                }
            }
        }
        printf("Case #%d: %lld\n",cas,getsum(n,m));
    }
    return 0;
}


你可能感兴趣的:(-----动规------)