hdu4352——數位dp+O(nlogn)求LIS+狀壓

題目鏈接:https://vjudge.net/problem/HDU-4352

題目大意:

給你一個區間[l,r],讓你求這個區間內符合條件的數的個數。

條件:這個數的數位上的LIS的長度等於k。

 題解:

一道很好的題目,需要知道怎麼O(nlogn)求LIS,這樣在才能進行狀態轉移。

如果不懂怎麼O(nlogn)求LIS,可以看下我的的博客:https://blog.csdn.net/qq_43472263/article/details/105084063

然後知道怎麼求之後我們可以考慮用一個9位的二進制數state表示(1,2,...9)哪些數字出現了。

舉個例子:110110000,其中1,2,4,5出現,其中1表示長度爲1的上升序列的最小最末元素(類比d數組),

4表示長度爲3(前面有3個1,包括自己)的上升序列的最小最末元素,2和5的定義也一樣。

在轉移狀態時,就需要根據上一個狀態和當前數位的值更新狀態就行了,

類比O(nlogn)的更新方法:如果前面沒有比他大的,就直接插入,否則把前面比他大的第一個元素改成它。

最後LIS的長度就是狀態state中的1的個數,其實也就是最後的d數組長度。

代碼實現:

#pragma GCC optimize(2)
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#define PI atan(1.0) * 4
#define E 2.718281828
#define rp(i, s, t) for (register int i = (s); i <= (t); i++)
#define RP(i, t, s) for (register int i = (t); i >= (s); i--)
#define ll long long
#define ull unsigned long long
#define mst(a, b) memset(a, b, sizeof(a))
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
#define pii pair<int, int>
#define mp make_pair
#define pb push_back
#define debug printf("ac\n");
using namespace std;
inline int read()
{
    int a = 0, b = 1;
    char c = getchar();
    while (c < '0' || c > '9')
    {
        if (c == '-')
            b = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
    {
        a = (a << 3) + (a << 1) + c - '0';
        c = getchar();
    }
    return a * b;
}
int a[20],num,k;
ll dp[20][1<<10][10];
int getnew(int x,int s){//類比O(nlogn)維護d數組的操作
    rp(i,x,9)
        if(s&(1<<i))//如果[x,9]有數出現,那麼就出現的數的位置變成0(s^(1<<i)),然後再把第x位變成1(state(|1<<x))
            return (s^(1<<i))|(1<<x);
	return s|(1<<x);//如果沒有數出現,證明當前數比之前的數都大,直接插入,把第x位變爲1(s|(1<<x))
}
int getnum(int x){//求狀態中1的個數
    int res=0;
    while(x){
        if(x&1) res++;
        x>>=1;
    } 
    return res;
}
ll dfs(int pos,int state,int k,int lead,int limit){
    if(pos==-1) return getnum(state)==k;
    if(!lead&&!limit&&dp[pos][state][k]!=-1) return dp[pos][state][k];
    int up=limit?a[pos]:9;
    ll ans=0;
    rp(i,0,up){
        ans+=dfs(pos-1,(lead&&i==0)?0:getnew(i,state),k,lead&&i==0,limit&&i==a[pos]);
    }
    if(!limit&&!lead) return dp[pos][state][k]=ans;
    return ans;
}
ll solve(ll x){
    num=0;
    while(x) a[num++]=x%10,x/=10;
    return dfs(num-1,0,k,1,1);
}
int main(){
    mst(dp,-1);
    ll l,r;
    int T=read();
    int kcase=0;
    while(T--){
        scanf("%lld%lld%d",&l,&r,&k);
        printf("Case #%d: %lld\n",++kcase,solve(r)-solve(l-1));
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章