题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5542
题目大意:
给一段长为n的序列,现在要找出m个严格递增的数,问这样的找法有多少种。
范围 :n<=1000,m<=1000,a[i]<=10^9
思路:
首先会往dp方面想,我们令f[i][j]表示前i个数字里面取了j个严格递增的数的找法。
但是为了严格递增,我们可以规定这个f[i][j]的最后那个数就是a[i]。
此时我们就可以得到转移方程:f[i][j]=∑f[k][j-1],其中1<=k<i,并且有a[k]<a[i]。
如此一来,我们就得到了一个O(n^3)的写法。
但是这样的话过不了这个题目。因为是求和,所以我们会想到利用树状数组在logn的时间里面完成。
这样一来总的复杂度就是O(n^2*logn)。
这里由于a[i]比较大,数的总量又比较小,所以我们可以先对数据进行离散化,将范围缩小到1000.
代码:
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<map>
#define mod 1000000007
#define ll long long
using namespace std;
int n,m;
ll f[1005][1005];
int lowbit(int x){
return x&(-x);
}
void add(int x,int y,ll tmp)
{
while(x<=n){
f[x][y]+=tmp;
f[x][y]=f[x][y]%mod;
x+=lowbit(x);
}
}
ll getsum(int x,int y)
{
ll ans=0;
while(x){
ans+=f[x][y];
ans=ans%mod;
x=x-lowbit(x);
}
return ans;
}
int main()
{
int i,j,k,a[1005],c[1005],T,aa[1005],icase=0,index;
scanf("%d",&T);
map<int,int>mp;
while(T--)
{
index=1;
icase++;
memset(f,0,sizeof(f));
memset(c,0,sizeof(c));
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
aa[i]=a[i];
}
sort(a+1,a+1+n);
k=1;
for(i=1;i<=n;i++)
{
aa[i]=lower_bound(a+1,a+1+n,aa[i])-a;
}
ll maxi=0,ans;
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
if(j>i)continue;
if(j==1)add(aa[i],1,1);
else
{
ans=getsum(aa[i]-1,j-1);
add(aa[i],j,ans);
}
}
}
maxi=getsum(n,m);
printf("Case #%d: ",icase);
printf("%lld\n",maxi%mod);
}
return 0;
}