给定一个长度为N的序列A,求A有多少个长度为M的严格递增子序列。

输入格式

第一行包含整数T,表示共有T组测试数据。

每组数据,第一行包含两个整数N和M。

第二行包含N个整数,表示完整的序列A。

输出格式

每组数据输出一个结果,每个结果占一行。

输出格式为“Case #x: y”,x为数据组别序号,从1开始,y为结果。

由于数据可能很大,请你输入对109+7取模后的结果。

数据范围

1≤T≤100,
1≤M≤N≤1000,
∑Ti=1Ni×Mi≤107
序列中的整数的绝对值不超过109。

输入样例:

2
3 2
1 2 3
3 2
3 2 1

输出样例:

Case #1: 3
Case #2: 0

思路: 求长度为n的序列中长度为m的严格递增子序列的个数可用dp来做:

状态表示:

用f[i][j]表示编号i前面的严格递增长度为j的子序列的个数。

状态计算:

根据i前面严格递增子序列的倒数第二个数a[k]的不同将编号i前面的严格递增长度为len的子序列的方案划分为k个不同的方案,假设a[k]<a[i],则最后一个递增长度的贡献者就是a[k]<a[i]这个关系且该个数为1是固定的,所以只需要再看前面长度为len-1严格递增序列的个数之和即可,即:f[i][len]=sum(f[k][len-1])   (其中a[k]<a[i], 1<=k<=i-1)

这里在求sum(f[k][len-1])时,其实就是求的一段在i之前的所有a[k]对应的长度为len-1的子序列个数构成的区间和,所以可以用树状数组维护a[k]对应的长度为len-1的子序列个数构成的区间和,o(logn)地查询即可,从而省去了一层o(n)的遍历,所以总的时间复杂度即为1e7*logn

注:因为a[i]的数值比较大,直接把a[i]的数值作为区间端点值可能会导致区间过大,扫描时时间超限,所以要把a数组离散化后再用树状数组维护。

完整代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#define lowbit(x) x&-x
using namespace std;const int maxn=1010,mod=1e9+7;int T,n,m;
int f[maxn][maxn],tree[maxn];
int nums[maxn],cnt,a[maxn];void insert(int x,int d)
{for(int i=x;i<=cnt;i+=lowbit(i)){tree[i]=(tree[i]+d)%mod;}
}int query(int x)
{int res=0;for(int i=x;i;i-=lowbit(i)){res=(res+tree[i])%mod;}return res;
}int main()
{ios::sync_with_stdio();cin.tie(0);scanf("%d",&T);for(int c=1;c<=T;c++){cin>>n>>m;cnt=0;for(int i=1;i<=n;i++){cin>>a[i];nums[cnt++]=a[i];}//离散化:sort(nums,nums+cnt);cnt=unique(nums,nums+cnt)-nums;for(int i=1;i<=n;i++) a[i]=lower_bound(nums,nums+cnt,a[i])-nums+1;memset(f,0,sizeof f);for(int i=1;i<=n;i++) f[i][1]=1;for(int len=2;len<=m;len++){for(int i=1;i<=cnt;i++) tree[i]=0;for(int i=1;i<=n;i++){f[i][len]+=query(a[i]-1);//dp:当前f[i][len]等于所有f[i][len-1]中满足a[k]<a[i]的个数之和insert(a[i],f[i][len-1]);//把当前数a[i]对应的其前面严格递增长度为len-1的个数f[i][len-1]插入到树状数组中}}int res=0;for(int i=1;i<=n;i++) res=(res+f[i][m])%mod;cout<<"Case #"<<c<<": "<<res<<endl;} return 0;
}

赤壁之战(dp树状数组)相关推荐

  1. 树形DP+树状数组 HDU 5877 Weak Pair

    1 //树形DP+树状数组 HDU 5877 Weak Pair 2 // 思路:用树状数组每次加k/a[i],每个节点ans+=Sum(a[i]) 表示每次加大于等于a[i]的值 3 // 这道题要 ...

  2. ACWING297. 赤壁之战(树状数组dp)

    给定一个长度为N的序列A,求A有多少个长度为M的严格递增子序列. 输入格式 第一行包含整数T,表示共有T组测试数据. 每组数据,第一行包含两个整数N和M. 第二行包含N个整数,表示完整的序列A. 输出 ...

  3. 小魂和他的数列(dp+树状数组优化)

    链接:https://ac.nowcoder.com/acm/contest/3566/C 来源:牛客网 Sometimes, even if you know how something's goi ...

  4. 牛客多校1 - Infinite Tree(虚树+换根dp+树状数组)

    题目链接:点击查看 题目大意:给出一个无穷个节点的树,对于每个大于 1 的点 i 来说,可以向点 i / minvid[ i ] 连边,这里的 mindiv[ x ] 表示的是 x 的最小质因数,现在 ...

  5. dp 树状数组 逆序元组

    wmq的队伍 发布时间: 2017年4月9日 17:06   最后更新: 2017年4月9日 17:07   时间限制: 2000ms   内存限制: 512M 描述 交大上课需要打卡,于是在上课前的 ...

  6. BZOJ.4553.[HEOI2016TJOI2016]序列(DP 树状数组套线段树/二维线段树(MLE) 动态开点)

    题目链接:BZOJ 洛谷 \(O(n^2)\)DP很好写,对于当前的i从之前满足条件的j中选一个最大值,\(dp[i]=d[j]+1\) for(int j=1; j<i; ++j)if(a[j ...

  7. HDU 2836 Traversal 简单DP + 树状数组

    题意:给你一个序列,问相邻两数高度差绝对值小于等于H的子序列有多少个. dp[i]表示以i为结尾的子序列有多少,易知状态转移方程为:dp[i] = sum( dp[j] ) + 1;( abs( he ...

  8. Codeforces 1096F(dp + 树状数组)

    题目链接 题意: 对于长度为$n$的排列,在已知一些位的前提下求逆序对的期望 思路: 将答案分为$3$部分 $1.$$-1$与$-1$之间对答案的贡献.由于逆序对考虑的是数字之间的大小关系,故假设$- ...

  9. 51nod 1680区间求和 (dp+树状数组/线段树)

    不妨考虑已知一个区间[l,r]的k=1.k=2....k=r-l+1这些数的答案ans(只是这一个区间,不包含子区间) 那么如果加入一个新的数字a[i](i = r+1) 则新区间[l, i]的答案为 ...

最新文章

  1. Python3通过汉字输出拼音
  2. 神经网络:你的大脑是一台计算机吗?
  3. DirectX 高级着色语言HLSL入门
  4. 知乎推荐页 Ranking 构建历程和经验分享
  5. ITK:在没有写访问权的情况下迭代图像中的区域
  6. 如何在CentOS 7上安装和配置MySQL Cluster
  7. Win8.1 JAVA环境配置全过程
  8. 数据分析如何揭示冠状病毒的真相?
  9. 如何在 Swift 中进行错误处理
  10. rpg制作大师_在线RPG大师班
  11. 阿里电话面试面试题总结,附答案!
  12. matlab运行时间特别长,Matlab运行时间过长
  13. gnome 如何自定义样式_在Gnome 3中自定义字体
  14. GB50016计算机房设计规定,为什么GB50016-2014《建筑设计防火规范》不包含防排烟系统实施规定?...
  15. 树突细胞会降解肿瘤抗原,阻断这一过程
  16. 【Linux】Linux私有组,主要组和附加组
  17. 移动CMPP3.0短信网关接口协议
  18. 吉林大学计算机研究生成绩计算方法,关于同济大学研究生成绩计算方法的说明...
  19. 读书笔记(二十二):前端安全
  20. c语言网络编程断点续传,网络编程(三) 下载任务,支持断点续传(示例代码)...

热门文章

  1. 与十俱进 2018双11狂欢看淘宝技术创新力
  2. linux桥接模式配置
  3. 解决Win11登录Microsoft账户一直转圈问题
  4. C++ 单词转换例子
  5. 糖儿飞教你学C++ Socket网络编程——2.本书目录
  6. HCNP学习笔记之OSPF邻接关系的建立和LSDB同步
  7. Word中的公式对齐
  8. 网易云音乐API使用
  9. HTML——表格、表格嵌套、表格布局
  10. 设计模式 -- 组合模式(Composite)