2947 MIS
题目描述 长度为m的递增子序列(M length IncreaseSubsequence),称为MIS。 求长度为n的序列,有多少个MIS。子序列不需要在原序列中不需要连续,但要保证相对位置。
输入 第一行两个整数n和m第二行n个整数,表示一个序列。
输出 输出MIS的个数,结果对20140921取余。
对于100%的数据,n的范围[1,10000],m的的范围[1,100],序列中的元素范围[1,109];
分析:
很容易写出状态转移方程:
for(int i=1;i<=n;i++)
for(int j=1;j<i;j++){
if(A[j]<A[i])
for(int k=1;k<m;k++)dp[k][i]=dp[k-1][j];
}
这样的复杂度是n*n*m
显然过不了
其实主要的复杂度堆在 查找前面比A[i]小的值和由转移数据上
这里很容易联想到线段树优化LIS
这样优化后的复杂度变成 n*logn*m 空间是4*m*n
然后就是dp 离散 线段树了。。。。。。
给出主函数(其他的自行yy)
int main(){
int n,m;
cin>>n>>m;
FOR(i,1,n)scanf("%d",&A[i]),B[i]=A[i];
sort(B+1,B+n+1);
int sum=unique(B+1,B+n+1)-B-1;
FOR(i,1,m)build(1,sum,1,i);
FOR(i,1,n){
int id=lower_bound(B+1,B+1+sum,A[i])-B;
update(id,1,1,1);
FOR(k,2,m){
if(id-1<1)break;
int res=query(1,id-1,1,k-1);
update(id,res,1,k);
}
}
int ans=query(1,sum,1,m);
printf("%d\n",ans);
}
小结:权值线段树应该是一个很常见的优化dp的方法,尤其是在查找前面区间并比较大小关系的问题,往往特别有用