传送门:C. Subsequences
描述:
给你n(1e5)个数a[],每个数都在[1,n]范围且数值各不相同。
我们想要求,长度为k+1(0<=k<=10)的LIS的数量是多少个
思路:
用dp[i][j]表示最后一个数的位置为i,长度为j的LIS数。
具体的转移方程则是f[i][j]=∑f[p][j-1],p<i且a[p]<a[i]。
然而这个DP方程却是O(n*n*k)的。
于是,我们要采取优化策略。我们需要找到在它之间比它小的数。
在它之间,可以通过扫描顺序解决。
而比它小,则可以通过树状数组动态更新和解决。
代码:
#include <bits/stdc++.h>
#define pr(x) cout << #x << "= " << x << " " ;
#define pl(x) cout << #x << "= " << x << endl;
#define ll __int64
using namespace std;
template<class T> void read(T&num) {
char CH; bool F=false;
for(CH=getchar();CH<'0'||CH>'9';F= CH=='-',CH=getchar());
for(num=0;CH>='0'&&CH<='9';num=num*10+CH-'0',CH=getchar());
F && (num=-num);
}
const int N=1e5+10;
int n,k;
ll dp[N][15];
void update(int i,int j,ll x){
while(i<=n){
dp[i][j]+=x;
i+=i&-i;
}
}
ll query(int i,int j){
ll s=0;
while(i>0){
s+=dp[i][j];
i-=i&-i;
}
return s;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
read(n);read(k);
update(1, 0, 1);
for(int i=1; i<=n; i++){
int x;read(x);
for(int j=k+1; j>=1; j--)update(x, j, query(x, j-1));//逆推
}
printf("%lld\n",query(n, k+1));
return 0;
}