題目大意:
給你一個區間[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; }
hdu4352——數位dp+O(nlogn)求LIS+狀壓
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.