解题思路
设 d p ( i , j ) dp(i,j) dp(i,j)表示以第i个数结尾。长度为j的严格上升子序列个数,则
- j=1时, d p ( i , j ) = 1 dp(i,j)=1 dp(i,j)=1
- j不等于时, d p ( k , j − 1 ) = dp(k,j-1)= dp(k,j−1)= ∑ k = 1 , a [ i ] > a [ k ] i − 1 \sum_{k=1,a[i]>a[k]}^{i-1} ∑k=1,a[i]>a[k]i−1
答案 a n s = ans= ans= ∑ i = 1 n \sum_{i=1}^{n} ∑i=1n d p ( i , m ) dp(i,m) dp(i,m)
然后我们将数组离散化,再建m个树状数组,分别维护长度为 1 , 2 , … m − 1 , m 1,2,…m-1,m 1,2,…m−1,m的方案数,用来优化 D P DP DP,复杂度 O ( T n m l o g n ) O(Tnmlogn) O(Tnmlogn)
代码
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define ll long long
using namespace std;
const int INF=1e9+7;
int T,n,m,t;
ll ans,f[1010][1010],tree[1010][1010];
struct c
{
int id,x;
} a[1010];
bool cmp(c l,c r)
{
if(l.x==r.x)return l.id>r.id;
return l.x<r.x;
}
int sum(int x,int y)
{
ll ans=0;
for(int i=x; i; i-=i&(-i))
ans=(ans+tree[i][y])%INF;
return ans%INF;
}
void add(int x,int y,int w)
{
for(int i=x;i<=n;i+=i&(-i))
tree[i][y]=(tree[i][y]+w)%INF;
}
int main()
{
scanf("%d",&T);
while(T--)
{
t++,ans=0;
memset(tree,0,sizeof(tree));
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i].x);
a[i].id=i;//离散化
}
sort(a+1,a+n+1,cmp);
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
{
if(j==1)f[i][j]=1;
else{f[i][j]=sum(a[i].id-1,j-1);
}
add(a[i].id,j,f[i][j]);
}
}
for(int i=m;i<=n;i++)
ans=(ans+f[i][m])%INF;
printf("Case #%d: %lld\n",t,ans);
}
}