XHXJ's LIS (状态压缩最长上升子序列)

题目链接:https://vjudge.net/contest/365059#problem/B

题目大意:求区间L到R之间的数A满足A的的数位的最长递增序列的长度为K的数的个数。

想法:

需要用 nlogn 的求最长上升子序列的算法去解决这道题,但是不能直接硬套,需要对状态进行压缩一下。

state状态维护的是前面上升子序列中出现的数字(二进制状态压缩),前面设状态为167(state为001100001),假设此时i=2,维护上升序列长度为3,应该把6变为2(此时state为001000011)

127,最长上升子序列长度不变,但能让后面更多的数加进来。

#pragma GCC optimize(3,"Ofast","inline")//O3优化
#pragma GCC optimize(2)//O2优化
#include <algorithm>
#include <string>
#include <string.h>
#include <vector>
#include <map>
#include <stack>
#include <set>
#include <queue>
#include <math.h>
#include <cstdio>
#include <iomanip>
#include <time.h>
#include <bitset>
#include <cmath>
#include <sstream>
#include <iostream>
#include <cstring>

#define LL long long
#define ls nod<<1
#define rs (nod<<1)+1
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define INF 0x3f3f3f3f
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)

const double eps = 1e-10;
const int maxn = 2e5 + 10;
const int mod = 1e9 + 7;

int sgn(double a){return a < -eps ? -1 : a < eps ? 0 : 1;}
using namespace std;

LL L,R,k;
int b[20];
int len;
LL mem[20][1100][15];

int cal(int x) {
    int ans = 0;
    while (x) {
        ans += (x & 1);
        x >>= 1;
    }
    return ans;
}

int new_st(int st,int x) {
    for (int i = x;i <= 9;i++) {
        if (st & (1 << i)) {
            return (st ^ (1 << i)) | (1 << x);
        }
    }
    return st | (1 << x);
}

LL dfs(int cur,int st,bool f,bool g) {
    if (cur < 0)
        return cal(st) == k;
    if (!f && mem[cur][st][k] != -1)
        return mem[cur][st][k];
    int v = 9;
    if (f)
        v = b[cur];
    LL ans = 0;
    for (int i = 0;i <= v;i++) {
        ans += dfs(cur-1,(g && i == 0)?0:new_st(st,i),f && (i==v),g && (i == 0));
    }
    if (!f)
        mem[cur][st][k] = ans;
    return ans;
}

LL solve(LL x) {
    len = 0;
    while (x) {
        b[len++] = x % 10;
        x /= 10;
    }
    return dfs(len-1,0,1,1);
}

int main() {
    ios::sync_with_stdio(0);
    int T;
    cin >> T;
    int t = 1;
    memset(mem,-1, sizeof(mem));
    while (T--) {
        cin >> L >> R >> k;
        printf("Case #%d: %lld
",t++,solve(R)-solve(L-1));
    }
    return 0;
}
原文地址:https://www.cnblogs.com/-Ackerman/p/12615277.html