题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5542
题目大意:在n个数中找长度为m的单调上升子序列有多少种方案
题目思路:DP,离散化,树状数组优化,
dp[i][j]代表大小为i的数 长度为j时的方案,状态转移方程dp[i][j]=simiga(dp[1..i-1][j-1]) 如果直接求和的话,复杂度是n^3不行
用树状数组优化求和 复杂度n^2logn
n<=1000,a[i]<=1e9,所以离散化搞一下就行
//更新一下 这题在HDU上面能过 但是在电科上面会T
//因为有个地方可以优化不少 在我们枚举当前大小为i长度为j的时候 也就是query(a[i]-1,j-1)时,如果该表达式为0,那么j-1之后的长度肯定时达不到的
直接break就行了
1 #include <stdio.h> 2 #include <string.h> 3 #include <iostream> 4 #include <algorithm> 5 using namespace std; 6 const int maxn=1100; 7 const long long mod=1e9+7; 8 long long tree[maxn][maxn]; 9 int dp[maxn][maxn]; 10 int a[maxn],b[maxn]; 11 void add(int i,int m,int k){ 12 for(i;i<=maxn;i+=(i&(-i))) (tree[m][i]+=k)%=mod; 13 } 14 long long query(int i,int m){ 15 long long sum=0; 16 for(i;i>0;i-=(i&(-i))) (sum+=tree[m][i])%=mod; 17 return sum%mod; 18 } 19 void init(int n,int m){ 20 for(int i=0;i<=m+1;i++){ 21 for(int j=0;j<=n+1;j++){ 22 dp[i][j]=0; 23 } 24 } 25 for(int i=0;i<=m+1;i++) 26 memset(tree[i],0,sizeof(tree[i])); 27 } 28 void solve(int T){ 29 printf("Case #%d: ",T); 30 int n,m; 31 scanf("%d %d",&n,&m); 32 init(n,m); 33 for(int i=1;i<=n;i++) scanf("%d",&b[i]),a[i]=b[i]; 34 sort(b+1,b+1+n); 35 int size=unique(b+1,b+1+n)-b; 36 for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+size,a[i])-b; 37 for(int i=1;i<=n;i++){ 38 for(int j=1;j<=min(i+1,m);j++){ 39 if(j==1) dp[j][a[i]]=1; 40 else 41 dp[j][a[i]]=query(a[i]-1,j-1)%mod; 42 if(dp[j][a[i]]==0) break;//如果长度j都到不了 那么j以后的肯定也是到不了 43 add(a[i],j,dp[j][a[i]]); 44 } 45 } 46 printf("%lld ",query(size,m)%mod); 47 } 48 int main(){ 49 int T; 50 scanf("%d",&T); 51 for(int i=1;i<=T;i++) solve(i); 52 }