题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4352

题目大意:

求区间 \([L,R]\) 范围内最长上升子序列(Longest increasing subsequence,简称LIS)长度为 \(k\) 的数的数量。

举个例子:

\(123\) 的LIS只有一个\(123\),所以它的LIS的长度是 \(3\);

\(101\) 的LIS只有一个\(01\),所以它的LIS的长度是 \(2\);

\(132\) 的LIS有\(13\)和\(12\),所以它的LIS的长度是 \(2\)。

现在每次给你三个数 \(L,R,k\) ,你要求区间 \([L,R]\) 范围内LIS长度为 \(k\) 的数有多少个。

解题思路:

本题使用 数位DP 进行求解。

但是我觉得比较必要的先决条件是:你要对如何 使用二分的方法求解LIS 有一个比较深刻的理解!

虽然这并不是必须的,但是这能够帮助你理解状态转移的过程。

设状态 \(dp[pos][sta][k]\) 表示对于当前的这个 \(k\):

  • 当前所处的数位为 \(pos\),
  • 当前LIS的状态为 \(sta\)

时的数量。

\(sta\) 涉及状态压缩的思想,他表示当前LIS中的元素由哪些组成。

一开始初始时候的 \(sta\) 为 \(0000000000\)(10个\(0\))。

在某一阶段,

如果当前已经选择了 \(a[0]\), \(a[1]\) 和 \(a[3]\) ,那么当前的状态就是 \(0000001011\);

如果当前已经选择了 \(a[2]\), \(a[4]\) 和 \(a[7]\) ,那么当前的状态就是 \(0010010100\)。

接下来我们来举例一个数 \(15234\) 来演示我们数位DP的过程:

初始时 \(sta\) 为 \(0000000000\);

加入 \(1\) ,此时状态变成 \(0000000010\);

加入 \(5\) ,此时状态变成 \(0000100010\);

加入 \(2\) ,此时状态变成 \(0000000110\),

注意:这里是最重要的地方!!

为什么加入 \(2\) 之后 \(5\) 对应的位置会变成 \(0\) 呢?

因为我们这里记录的状态就是我们二分LIS对应的状态,

刚加入 \(5\) 的时候,状态是 \(0000100010\) ,它表示新加入的元素要构成一个长度为 \(2\) 的LIS,必须比 \(1\) 大,

新加入的元素要构成一个长度为 \(3\) 的LIS,必须比 \(5\) 大。

而加入 \(2\) 之后,情况就大大改观了,因为此时要构成一个长度为 \(2\) 的LIS,只需要比 \(2\) 大就可以了。

所以对于当前状态 \(sta\) 和当前数位要放的数字 \(i\) ,

如果 \(sta\) 的第 \(i\) 位为 \(1\) ,那么新的状态仍旧是 \(sta\)(因为LIS中存在 \(i\));

如果 \(sta\) 的第 \(i\) 为为 \(0\) ,那么:

  • 如果 \(sta\) (没有特别强调都是指二进制)中没有任何比 \(i\) 大的位上为 \(1\) ,则新状态就是 sta | (1<<i)
  • 否则,将比 \(i\) 大的最小的那位置为 \(0\),再将第 \(i\) 位置为 \(1\),就是新的状态。

实现代码如下:

#include <bits/stdc++.h>
using namespace std;
long long f[22][1030][10];
int n, k, a[22];
void init() {
memset(f, -1, sizeof(f));
}
int new_sta(int pos, int sta, int i) {
if (!sta && i==0 && pos>0) return 0;
if (!(sta>>(i+1)) || (sta&(1<<i))) return sta | (1<<i);
for (int k = k = i+1; k < 10; k ++) if (sta & (1<<k)) return (sta ^ (1<<k)) | (1<<i);
}
long long dfs(int pos, int sta, bool limit) {
if (pos < 0) return __builtin_popcount(sta) == k ? 1 : 0;
if (!limit && f[pos][sta][k] != -1) return f[pos][sta][k];
int up = limit ? a[pos] : 9;
long long tmp = 0;
for (int i = 0; i <= up; i ++) {
tmp += dfs(pos-1, new_sta(pos, sta, i), limit && i==up);
}
if (!limit) f[pos][sta][k] = tmp;
return tmp;
}
long long get_num(long long x) {
int pos = 0;
while (x) {
a[pos++] = x % 10;
x /= 10;
}
return dfs(pos-1, 0, true);
}
int T;
long long L, R;
int main() {
init();
scanf("%d", &T);
for (int cas = 1; cas <= T; cas ++) {
scanf("%lld%lld%d", &L, &R, &k);
printf("Case #%d: %lld\n", cas, get_num(R) - get_num(L-1));
}
return 0;
}

HDU4352 XHXJ's LIS 题解 数位DP的更多相关文章

  1. hdu4352 XHXJ's LIS(数位dp)

    题目传送门 XHXJ's LIS Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  2. 【HDU 4352】 XHXJ's LIS (数位DP+状态压缩+LIS)

    XHXJ's LIS Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  3. hdu 4352 "XHXJ's LIS"(数位DP+状压DP+LIS)

    传送门 参考博文: [1]:http://www.voidcn.com/article/p-ehojgauy-ot.html 题解: 将数字num字符串化: 求[L,R]区间最长上升子序列长度为 K ...

  4. 【HDU】4352 XHXJ's LIS(数位dp+状压)

    题目 传送门:QWQ 分析 数位dp 状压一下现在的$ O(nlogn) $的$ LIS $的二分数组 数据小,所以更新时直接暴力不用二分了. 代码 #include <bits/stdc++. ...

  5. HDU - 4352 - XHXJ's LIS(数位DP)

    链接: https://vjudge.net/problem/HDU-4352 题意: a 到 b中一个数组成递增子序列长度等于k的数的个数 思路: 因为只有10个数,使用二进制维护一个递增序列,每次 ...

  6. hdu 4352 XHXJ's LIS (数位dp+状态压缩)

    Description #define xhxj (Xin Hang senior sister(学姐)) If you do not know xhxj, then carefully readin ...

  7. HDU 4352 XHXJ's LIS (数位DP,状压)

    题意: 前面3/4的英文都是废话.将一个正整数看成字符串,给定一个k,问区间[L,R]中严格的LIS=k的数有多少个? 思路: 实在没有想到字符0~9最多才10种,况且也符合O(nlogn)求LIS的 ...

  8. hdu4352 XHXJ's LIS(数位DP + LIS + 状态压缩)

    #define xhxj (Xin Hang senior sister(学姐)) If you do not know xhxj, then carefully reading the entire ...

  9. hdu4352 XHXJ's LIS[数位DP套状压DP+LIS$O(nlogn)$]

    统计$[L,R]$内LIS长度为$k$的数的个数,$Q \le 10000,L,R < 2^{63}-1,k \le 10$. 首先肯定是数位DP.然后考虑怎么做这个dp.如果把$k$记录到状态 ...

随机推荐

  1. vue 后期追回的属性不更新视图问题

    this.$set(this.problemList[index], 'sub', [])   因为原始数组是有set,get而追加的没有,所以需要用这种方式   // 添加 this.$set(th ...

  2. Android的headerView和emptyView共存问题

    今天做项目的时候,准备优化下ListView相关的东西,于是乎,需要做一个当列表无数据时,空的提醒页面.这个自然想到的是ListView的setEmptyView()方法,于是顺手就写了,可是,当我为 ...

  3. 谷歌BERT预训练源码解析(二):模型构建

    目录前言源码解析模型配置参数BertModelword embeddingembedding_postprocessorTransformerself_attention模型应用前言BERT的模型主要 ...

  4. ORA-01089: 即時シャットダウン処理中 - 操作はできません

    一:当时的情景 SQL> shutdown immediate --无任何返回结果 二:问题定位过程 1.查询相关进程只有ORACLE的关键进程存在 ps -ef |grep ora_ soad ...

  5. 2016 年度开源中国新增开源软件排行榜 TOP 100

    2016 年度开源中国新增开源软件排行榜 TOP 100 2016 年度开源中国新增开源软件排行榜 TOP 100 新鲜出炉!本榜单根据 2016 年开源中国新收录的 3030 款软件的关注度和活跃度 ...

  6. 学习HTML<audio>标签

    首先来看下这个例子: <audio controls autoplay="autoplay"> <source src="horse.ogg" ...

  7. Vue 用第三方的库去实现动画效果

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. python模块之序列化模块

    序列化 """ 序列--字符串 序列化--其他数据类型转化为字符串数据类型 反序列化--字符串转化为其他数据类型 """ json模块 &q ...

  9. 从http到https--phpStudy2018

    0. 将SSL证书解压到以下目录,申请方式见 百度 Apache/cert/ 分别更名为 my_public.crt my.key my_chain.crt 1. phpStudy->其它选项菜 ...

  10. 记录vue创建项目过程

    已经学过无数次,但是每次都忘记,毕竟脑容量太小了,每次都需要翻看原来项目和视频再次学习,所以以此文字形式记录下来,方便于下次使用观看 1.打开git,找到创建vue的文件夹(已经安装好git的,然后在 ...