这是个计数的问题,我们考虑dp方程 表示长度为 i ,以 aj 结尾的严格上升子序列的个数
显然: 其中 k < j && a[ k ] < a[ j ]
朴素代码:
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++) scanf("%d",&a[i]);
memset(dp,0,sizeof(dp));
dp[0][0]=1;
for(int i = 1; i <= m; i++){
for(int j = 1; j <= n; j++){
for(int k = 0; k < j; k++)
if(a[k]<a[j]) (dp[i][j]+=dp[i-1][k])%mod;
}
}
ll ans = 0;
for(int i = 1; i <= n; i++)
(ans+=dp[m][i])%mod;
printf("Case #%d: %lld\n",++as,ans);
这复杂度显然不行啊,最多n^3,我们发现最内层循环类似一个偏序的问题 我们拿个树状数组就可以轻松优化了
为了满足树状数组从0开始的特点 以及dp的初始化 我们需要在a数组中插入一个0 放在最前面 这样离散化后它的下标就是1不会对树状数组的建立造成影响
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1010;
int a[N],n,m,b[N];
const ll mod = 1e9+7;
ll dp[N][N],c[N];
void add(int x,ll val){
while(x<=n+3){
c[x]+=val;
c[x]%=mod;
x+=x&-x;
}
}
ll query(int x){
ll ret = 0;
while(x){
ret+=c[x];
ret%=mod;
x-=x&-x;
}
return ret;
}
int main(){
int t,as=0;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++) scanf("%d",&a[i]),b[i+1]=a[i];
sort(b+2,b+2+n);
b[1]=a[0]=0;
for(int i = 0; i <= n; i++) a[i]=lower_bound(b+1,b+2+n,a[i])-b;
memset(dp,0,sizeof(dp));
dp[0][0]=1;
for(int i = 1; i <= m; i++){
memset(c,0,sizeof(c));
add(a[0],dp[i-1][0]);
for(int j = 1; j <= n; j++){
dp[i][j]=query(a[j]-1);
add(a[j],dp[i-1][j]);
}
}
ll ans = 0;
for(int i = 1; i <= n; i++)
ans=(ans+dp[m][i])%mod;
printf("Case #%d: %lld\n",++as,ans);
}
return 0;
}