HDU 5542 树状数组优化dp

这是个计数的问题,我们考虑dp方程 dp[i][j]表示长度为 i ,以 aj 结尾的严格上升子序列的个数 

显然: dp[i][j]+=dp[i-1][k]  其中  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;
}
原创文章 85 获赞 103 访问量 2496

猜你喜欢

转载自blog.csdn.net/weixin_43824564/article/details/105614343
今日推荐