【HDU5542 2015 CCPC 南阳国赛C】【DP】The Battle of Chibi n个数中恰好长度为m的单升子序列数

#include
#include
#include
using namespace std;
const int Z=1e9+7;
const int N=1005;
int casenum,casei;
int n,m;
int a[N];
int f[N][N];
int d[N][N];int top;
#include
mapmop;
map::iterator it;
inline void add(int &x,int y)
{
	x+=y;
	if(x>=Z)x-=Z;
}
int cnt(int p,int x)
{
	long long tmp=0;
	for(;x;x-=x&-x)tmp+=d[p][x];
	return tmp%Z;
}
void add(int p,int x,int v)
{
	for(;x<=top;x+=x&-x)add(d[p][x],v);
}
int main()
{
	scanf("%d",&casenum);
	for(casei=1;casei<=casenum;casei++)
	{
		scanf("%d%d",&n,&m);
		mop.clear();
		for(int i=1;i<=n;i++)scanf("%d",&a[i]),mop[a[i]]=0;
		top=0;for(it=mop.begin();it!=mop.end();it++)it->second=++top;
		for(int i=1;i<=n;i++)a[i]=mop[a[i]];
		
		memset(d,0,(m+1)*N*4);
		memset(f,0,(n+1)*N*4);
		int ans=0;
		for(int i=1;i<=n;i++)
		{
			int top=min(i,m);
			for(int j=1;j<=top;j++)
			{
				if(j==1)f[i][j]=1;
				else add(f[i][j],cnt(j-1,a[i]-1));
				add(j,a[i],f[i][j]);
			}
			add(ans,f[i][m]);
		}
		printf("Case #%d: %d\n",casei,ans);
	}
	return 0;
}
/*
【题意】
给你n(1e3)个数,每个数都在[1,1e9]范围。
然后让你保持数的顺序不变,选出长度恰好为m(1<=m<=n)的单调上升子序列。
问你有多少种选择方案

【类型】
DP
最长上升子序列
离散化+树状数组

【分析】
这道题一开始我是以O(nlogn)最长上升子序列的插入法做。但是很难实现。卡了一段时间。

后来结合n只有1000的情况,我们应该设想到O(n^2)的做法。
题目涉及到选择,我们不妨回归到一个很经典的DP模型——
用f[i][j]表示前i个数中选择了j个数的上升子序列的方案数。
这个状态的一个好处是,它把这个上升自序列的长度也包含了,就是j。
还有一个性质,我们如何查看一个数能否接在前面的序列后?其实只要知道之前上升子序列的最后一位就可以进行判定了。
鉴于这个,我们修改下f[i][j]的含义——
f[i][j]表示前i个数中选择了j个数,且最后一个数严格为a[i]时的上升子序列方案数。
这样就有状态转移f[i][j]=∑f[p][j-1],p∈[1,i-1]且a[p]=1;j--)
	{
		if(j==1)f[i][j]=1;
		else add(f[i][j],cnt(j-1,a[i]-1));
		add(j,a[i],f[i][j]);
	}
	add(ans,f[i][m]);
}

【时间复杂度&&优化】
O(n^2 logn)
取模还是很消耗时间的233

*/

你可能感兴趣的:(题库-HDU,动态规划-线性DP)