HDU 4991 Ordered Subsequence

Problem Description
A numeric sequence of a i is ordered if a 1<a 2<……<a N. Let the subsequence of the given numeric sequence (a 1, a 2,……, a N) be any sequence (a i1, a i2,……, a iK), where 1<=i 1<i 2 <……<i K<=N. For example, sequence (1, 7, 3, 5, 9, 4, 8) has ordered subsequences, eg. (1, 7), (3, 4, 8) and many others. 

Your program, when given the numeric sequence, must find the number of its ordered subsequence with exact m numbers.
 

Input
Multi test cases. Each case contain two lines. The first line contains two integers n and m, n is the length of the sequence and m represent the size of the subsequence you need to find. The second line contains the elements of sequence - n integers in the range from 0 to 987654321 each.
Process to the end of file.
[Technical Specification]
1<=n<=10000
1<=m<=100
 

Output
For each case, output answer % 123456789.
 

Sample Input
 
  
3 2 1 1 2 7 3 1 7 3 5 9 4 8
 

Sample Output
 
  
2 12

给出n个数的序列,给定m,求这n个数中有多少个长度为m的严格上升子序列.

(1<=n<=10000  1<=m<=100 序列中的数字x满足0<=x<=987654321,结果对123456789取余)

比如 序列 1 1 2 n=3,m=2 那么满足的序列有 1 2 和 1 2 一共有两个。

统计问题向树状数组那方面想,如果把输入的x看作一个位置的话,那最多需要开辟987654321个位置,假如是这样的话那我们就可以利用桶排序的思想来做这个题,而实际上我们开不出来这样的大数组. 考虑统计的时候按该数的数值大小在所有数中排第几(用数组rank表示,从小到大)来进行开辟位置,那么最多开辟10000个位置。也就是将具体的数和该数在所有数中的rank进行映射。

比如数  1 4 4 2 8 那么 1的rank为1,2的rank为2,4的rank为3,8的rank为4,这样每个数都有对应rank的映射 1 3 3 2 4。

令dp[i][j] 表示以第i个数结尾且长度为j的上升子序列,1<=i<=n,  1<=j<=m。

那么有递推公式  dp[i][j]= ∑ dp[k][j-1]  (  1<=k<=i-1&& num[k]<num[i])  num[i]为输入的数

一开始dp[i][1]=1;1<=i<=n,其它的全部初始化为0.

以1 4 4 2  m=2为例说明一下树状数组的统计过程:首先准备工作, 数和rank映射 mp[1]=1 mp[2]=2 mp[4]=3 。对应到输入序列的每个位置上则是 p[1]=1 p[2]=3 p[3]=3 p[4]=2 。dp[1][1]=1 dp[2][1]=1  dp[3][1]=1 dp[4][1]=1。

dp[i][j] 当j=2时,遍历n,dp[1][2],首先第一个数1在树状数组中的位置排第一,此时它前面没有数,即dp[1][2]=0

然后第二个数4在树状数组中的位置排第3,此时它前面有1个1,且那个1表示长度为1且1<4,所以dp[2][2]=1

然后dp[3][2]=1

然后dp[4][2]=1

所以一共的符合条件的序列个数为 ∑dp[i][m]

#include <iostream> #include <stdio.h> #include <algorithm> #include <string.h> #include <map> using namespace std; #define mod 123456789 int dp[10001][101];//dp[i][j]代表以第i个数结尾的长度为j的上升子序列的个数 int num[10001];//输入的原始数据 int N;//消除重复元素后还剩多少个数 map<int,int> mp;//这个地方不能将mp开成数组形式,因为我们开不了那么大. int pos[10001];//第i个位置的数在所有数中排第几个 int c[10001];//树状数组部分 int lowbit(int x) {     return x&(-x); } int sum(int p) {     int ans=0;     while(p>0)     {         ans=(ans%mod+c[p]%mod)%mod;         p=p-lowbit(p);     }     return ans; } void add(int p,int val) {     while(p<=N)     {         c[p]=(c[p]+val)%mod;         p=p+lowbit(p);     } } int main() {     int n,m;     while(scanf("%d%d",&n,&m)!=EOF)     {         memset(dp,0,sizeof(dp));         int temp[10001];//num的备份数据用来去重操作         for(int i=1;i<=n;i++)         {             scanf("%d",&num[i]);             temp[i]=num[i];             dp[i][1]=1;         }         sort(temp+1,temp+1+n);         N=unique(temp+1,temp+1+n)-(temp+1);         for(int i=1;i<=N;i++)             mp[temp[i]]=i;//具体的数在所有数中排第几         for(int i=1;i<=n;i++)             pos[i]=mp[num[i]];//输入位置和排第几映射         for(int j=2;j<=m;j++)         {             memset(c,0,sizeof(c));             for(int i=1;i<=n;i++)             {                 dp[i][j]=sum(pos[i]-1);//求小于num[i]且长度为j-1的和                 add(pos[i],dp[i][j-1]); //把c数组的pos[i]位置更新成dp[i][j-1]             }         }         int ans=0;         for(int i=1;i<=n;i++)             ans=(ans%mod+dp[i][m]%mod)%mod;         cout<<ans<<endl;     }     return 0; }

转载于:https://www.cnblogs.com/MisdomTianYa/p/6581729.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值