HDU 4352 XHXJ's LIS(数位dp&状态压缩)

题目链接[kuangbin带你飞]专题十五 数位DP B - XHXJ’s LIS

题意

给定区间。求出有多少个数满足最长上升子序列(将数看作字符串)的长度为k。

思路

一个数的上升子序列最大长度为10,所以每个上升子序列的状态都能够用10个二进制位来表示。

上升子序列的变化能够用LIS的方式来更新。

dp[len][num][k]

len为当前的位,num为当前上升子序列的状态。k表示子序列的长度。

next[s][num]为记录预处理的子序列的状态变化。

cnt [num]记录各个状态的最长上升子序列的长度。

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <vector>

using namespace std;

#define LL long long

LL dp[20][1<<10][11];
int dis[20];
int cnt[1<<10];
int nxt[10][1<<10];

int getnext(int num, int s)
{
    for(int i=s; i<10; i++)
    {
        if(num & (1<<i))
            return (num^(1<<i)) | 1<<s;
    }
    return num | 1<<s;
}

LL dfs(int k, int len, int num, bool flag, bool zero)
{
    if(len < 0)
        return cnt[num] == k;
    if(!flag && dp[len][num][k]!=-1)
        return dp[len][num][k];
    LL ans = 0;
    int end = flag?dis[len]:9;
    for(int i=0; i<=end; i++)
        ans += dfs(k, len-1, (zero&&i==0)?

num:nxt[i][num], flag&&i==end, zero&&i==0);
    if(!flag)
        dp[len][num][k] = ans;
    return ans;
}

LL solve(LL n, int k)
{
    int pos = 0;
    while(n)
    {
        dis[pos++] = n%10;
        n /= 10;
    }
    return dfs(k, pos-1, 0, 1, 1);
}

void init()
{
    memset(dp, -1, sizeof(dp));
    for(int i=0; i<1<<10; i++)
    {
        cnt[i] = 0;
        for(int j=0; j<10; j++)
        {
            if(i & (1<<j))
                cnt[i]++;
            nxt[j][i] = getnext(i, j);
        }
    }
}

int main()
{
    int T;
    scanf("%d", &T);
    init();
    for(int i=1; i<=T; i++)
    {
        long long l, r, k;
        scanf("%lld%lld%lld", &l, &r, &k);
        printf("Case #%d: %lld\n", i, solve(r, k)-solve(l-1, k));
    }
    return 0;
}

HDU 4352 XHXJ's LIS(数位dp&amp;状态压缩)

时间: 2024-08-03 15:22:58

HDU 4352 XHXJ&#39;s LIS(数位dp&amp;状态压缩)的相关文章

SPOJ BALNUM Balanced Numbers(数位dp,状态压缩)

BALNUM - Balanced Numbers no tags 题目链接 Balanced numbers have been used by mathematicians for centuries. A positive integer is considered a balanced number if: 1)      Every even digit appears an odd number of times in its decimal representation 2)   

[CF55D]Beautiful numbers(数位dp,状态压缩)

题目链接:http://codeforces.com/problemset/problem/55/D 题意:给定区间,求区间内某数的所有数位能整除这个数本身的数的个数. 起初思路:dp(l,s,sum)表示这个数到l位,并且0~9出现的状态s,和为sum的时候的数字个数.发现这个sum不好处理,因为数字越来越大无法保证这个界限.用到一个性质:一个数能被一堆数整除,当且仅当这个数能被这堆数的最小公倍数整除.换句话说,我们统计这个数对1~9的最小公倍数取模,就能判断这个sum是否可以被各位数整除了.

HDU 4352 XHXJ&#39;s LIS (数位DP,状压)

题意: 前面3/4的英文都是废话.将一个正整数看成字符串,给定一个k,问区间[L,R]中严格的LIS=k的数有多少个? 思路: 实在没有想到字符0~9最多才10种,况且也符合O(nlogn)求LIS的特点,所以用状态压缩可以解决. 看到状态压缩的字眼基本就会做了,增加一维来保存当前LIS的状态.由于求LIS时的辅助数组d[i]表示长度为i的LIS最后一个元素,d数组是严格递增的,所以好好利用d数组的特性来设计状态压缩才是关键.压缩的状态0101可以表示:仅有0和2在数组d中,即d[1]=0,d[

HDU 4352 XHXJ&#39;s LIS 数位DP + 状压

由LIS的nlogn解法 可以得出最后统计数组中数的个数即为LIS的长度 这样就可以状压了 #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #include <iostream> #include <map> #include <c

HDU 4352 XHXJ&#39;s LIS 数位dp

题目链接:点击打开链接 题意: 一个数自身的最长子序列=每一位都是一个数字然后求的LIS 问区间内有多少个数 自身的最长子序列==k 思路: 因为自身的最长子序列至多=10,且由0~9组成,所以状压10个二进制表示0~9中哪些数字已经用过 dp[i][j] 表示长度为i的数字,最长子序列中出现的数字状态j的方法数.由于询问数=K,也存下来避免重复计算. #include <cstdio> #include <algorithm> #include <cstring> #

[数位dp+状态压缩] hdu 4352 XHXJ&#39;s LIS

题意: 给x.y.k,在[x,y] 范围内最长上升子序列长度是k的数有几个 思路: 模仿 LIS nlogn的想法,这里就只有10个数,进行状压 然后直接搜就好了不用二分 然后按位dp下去就ok了! 代码: #include"cstdlib" #include"cstdio" #include"cstring" #include"cmath" #include"queue" #include"al

hdu 4352 XHXJ&#39;s LIS (数位dp)

XHXJ's LIS Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 755    Accepted Submission(s): 289 Problem Description #define xhxj (Xin Hang senior sister(学姐)) If you do not know xhxj, then careful

HDU.4352.XHXJ&#39;s LIS(数位DP 状压 LIS)

题目链接 数位DP. 至于怎么求LIS,因为只有10个数,所以可以参照O(nlogn)求LIS的方法,状压记录状态. 每次加一个数和求LIS一样更新状态.最后状态中1的个数就是LIS的长度. //93MS 3004K #include <cstdio> #include <cctype> #include <cstring> #include <algorithm> #define gc() getchar() typedef long long LL; c

HDU - 4352 - XHXJ&#39;s LIS(数位DP)

链接: https://vjudge.net/problem/HDU-4352 题意: a 到 b中一个数组成递增子序列长度等于k的数的个数 思路: 因为只有10个数,使用二进制维护一个递增序列,每次更新在注释写了. 然后正常的数位DP, Dp(i, j, k),i是位置,j是当前的递增状态,k是长度. 考虑一下前缀0,重置状态 代码: #include<bits/stdc++.h> using namespace std; typedef long long LL; const int MO