CF597C Solution
题解
容易看出此题为dp问题,利用LIS的思想可设计出如下\(O(n^2k)\)的算法。
状态:\(dp[i][j]\)表示以\(a_i\)为结尾、长度为\(j\)的上升子序列个数。
初始值:\(dp[i][1]=1,dp[0][j]=0\quad(1\le i\le n,1\le j\le k)\)。
转移方程:\(dp[i][j]=\sum\limits_{p=1}^{i-1} dp[p][j-1]\quad (1\le i\le n,2\le j\le min(i,k),a_p<a_i)\)。
答案:\(ans=\sum\limits_{i=k}^ndp[i][k]\)。
其中转移方程仅为一个前缀和,可以想到建一颗\(k\)维的树状数组存储\(dp\)值,每次查询并更新即可在\(O(logn)\)的时间中完成转移。
AC代码
#include<bits/stdc++.h>
#define int long long
#define lowb(x) x&(-x)
using namespace std;
const int N=1e5+10,K=15;
int a[N],dp[N][K],t[N][K],n; //t[][j]:dp[][j]所对树状数组
int query(int x,int k)
{
int ans=0;
while(x) {ans+=t[x][k]; x-=lowb(x);}
return ans;
}
void add(int x,int d,int k)
{
while(x<=n) {t[x][k]+=d; x+=lowb(x);}
}
signed main()
{
int k,ans=0;
scanf("%lld%lld",&n,&k); k++;
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)
{
dp[i][1]=1; add(a[i],1,1);
for(int j=2;j<=min(i,k);j++)
{
dp[i][j]=query(a[i]-1,j-1);
add(a[i],dp[i][j],j);
}
}
for(int i=k;i<=n;i++) ans+=dp[i][k];
printf("%lld",ans);
return 0;
}