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;
}
原文地址:https://www.cnblogs.com/violetholmes/p/14385942.html