HDU 4352 XHXJ's LIS - 状压dp + LIS

传送门

题目大意:

求[l, r]中数位的最长上升序列恰好为k的数的个数。

题目分析:

首先要理解\(o(nlogn)\)求LIS问题的思路,每次寻找第一个大于等于的数将其更改。
设dp[pos][sta][k]表示第pos位,sta见后,加入k是为了初始化减少后面的时间。
sta表示前pos位的LIS,是一个2进制数,110位表示09是否被选,比如现在状态是0010100000,就是说LIS是2,4,分两种情况:

  • 现在加入一个数字3,用LIS的方法找到第一个大于等于3的数----4,将其置为0,并把3置为1,sat变为0011000000.
  • 若加入6,没有大于等于它的数,就直接将6置为1,sat变为0010101000
    需要先预处理to[i][j]表示i状态加入数字j后转移到的状态来加速。 我卡了半天T所以必须初始化。

code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;

struct ioSys{
    inline ll read(){
        ll i = 0, f = 1; char ch = getchar();
        for(; (ch < '0' || ch > '9') && ch != '-'; ch = getchar());
        if(ch == '-') f = -1, ch = getchar();
        for(; ch >= '0' && ch <= '9'; ch = getchar())
            i = (i << 3) + (i << 1) + (ch - '0');
        return i * f;
    }
    inline void wr(int x){
        if(x < 0) putchar('-'), x = -x;
        if(x > 9) wr(x / 10);
        putchar(x % 10 + '0');
    }
    inline void operator >> (ll &x){
        x = read();
    }
    inline void operator << (int x){
        wr(x);
    }
}IO;

ll T;
ll l, r, k, len;
typedef long long ll;
ll dp[25][1050][15], s[25], to[1050][15], oneNum[1050];

inline void convert(ll x){
    len = 0;
    memset(s, 0, sizeof s);
    while(x) s[++len] = x % 10, x /= 10;
}

inline ll count(ll x){
    int ret = 0;
    while(x) ret++, x = x & (x - 1);
    return ret;
}

inline void init(){
    memset(dp, -1, sizeof dp);
    for(int i = 0; i <= (1 << 10) - 1; i++){
        for(int j = 0; j <= 9; j++){
            int  tmp = 0, p;
            for(p = j; p < 10; p++)
                if(i & (1 << p)) break;
            if(p == 10) tmp = i | (1 << j);
            else tmp = i ^ (1 << p) | (1 << j);
            to[i][j] = tmp;
        }
    }
    for(int i = 0; i <= (1 << 10) - 1; i++)
        oneNum[i] = count(i);
}

inline ll DP(int pos, int sta, bool limit, bool lead, int K){
    if(!limit && !lead && dp[pos][sta][K] != -1) return dp[pos][sta][K];
    if(pos == 0) return oneNum[sta] == k || (lead && K == 1);
    int high = limit ? s[pos] : 9;
    ll ret = 0;
    for(int i = 0; i <= high; i++){
        if(i == 0 && lead) ret += DP(pos - 1, 0, limit && (i == high), true, K);
        else
            ret += DP(pos - 1, to[sta][i], limit && (i == high), false, K);
    }
    if(!limit && !lead) dp[pos][sta][K] = ret;
    return ret;
}

int main(){
   T = IO.read();
   init();
   for(int t = 1; t <= T; t++){
       IO >> l, IO >> r, IO >> k;
       convert(r);
       ll ret1 = DP(len, 0, 1, 1, k);
       convert(l - 1);
       ll ret2 = DP(len, 0, 1, 1, k);
       printf("Case #%d: %I64d\n", t, ret1 - ret2);
   }
   return 0;
}

posted @ 2017-10-13 17:06  CzYoL  阅读(136)  评论(0编辑  收藏  举报