题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5542
求一个长度为n的序列的长度为m的严格上升子序列的数量,dp的状态是前i个数中长度为j的严格上升子序列且以第i个数结尾的决策数量。
转移方式:长度为i-1向长度为i转移,枚举位置比它小而且值比它小的决策的数量,通过树状数组将一个维度优化成为O(logn)级别,最终时间复杂度大约是O(n*mlogn)。
处理这样决策集合只会增大并且只会有一个候选项加到决策集合中的问题,可以用树状数组代表一个位置变量,然后一个维度线性增长,每次添加进一个目标值,这样就可以查询前i个目标值中不超过给定值的前缀和。
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 1010,mod=1e9+7; int c[N]; int a[N]; int num[N]; int cnt,n,m; int f[N][N]; int lowbit(int x){ return x & -x; } int query(int x){ int ans=0; while(x){ ans=(ans+c[x])%mod; x-=lowbit(x); } return ans; } void update(int x,int C){ while(x<=cnt){ c[x]=(c[x]+C)%mod; x+=lowbit(x); } } int main(){ int T; cin>>T; for(int C=1;C<=T;C++){ cin>>n>>m; for(int i=1;i<=n;i++)scanf("%d",&a[i]); //离散化 memcpy(num,a,sizeof(a)); sort(num+1,num+n+1); cnt=unique(num+1,num+n+1)-(num+1); for(int i=1;i<=n;i++) a[i]=lower_bound(num+1,num+cnt+1,a[i])-num; for(int i=1;i<=n;i++)f[i][1]=1; for(int j=2;j<=m;j++){ memset(c,0,sizeof c); for(int i=1;i<=n;i++){ f[i][j]=query(a[i]-1);//小于a[i]的前缀和 update(a[i],f[i][j-1]); } } int ans=0; for(int i=1;i<=n;i++) ans=(ans+f[i][m])%mod; printf("Case #%d: %d ",C,ans); } return 0; }