題意:求出給定區間[L,R] (0<L<=R<263-1 and 1<=K<=10) 中數滿足LIS(非連續嚴格遞增子序列)為K的個數?
如區間[123,321]中的124的LIS為3;
<1> 總結數位DP的優化方式:
例: 1.在codeforces 的beautiful number 中,要求滿足能被自己的每一個非零數字整除的數的個數。這時構造出lcm,當高位mod(所有元素的lcm直接優化到2520)後將會出現很多重疊的子問題,這就是狀態的轉移;當然裡面各種的lcm只需存儲即可;
2.本題:首先要知道給定一個序列,知道怎麼求解LIS,如 1 2 4 6 ,當加入3時,這時長度為3的LIS的最小值為3,而不是4.(使用數組存儲的是[長度] = minval),但是本題並不是這樣的LIS,這需要模糊看待才能將不一樣的高位看成是相同的,才能有重疊的子結構.
在dfs開始時,state為0,這時如果前面加入的是98之後加入1,那麼按照原始的LIS長度是為1的,但是這裡長度看成3,為什麼?因為這裡state存儲的只是狀態,就是說dfs到pos-1位時,只知道前面有哪些值,並不需要知道加入的前後順序~~那這就將排序問題簡化為了組合問題,這才是優化的本質。這也是為什麼說state中的1個數就是LIS的值;(故意按照遞增排列的)
但是還有問題:
到此,就可以確定dfs的參數為4個pos,state,edge(原來就有),還要考慮一個是否有前置0的zero參數。
(使用的是內置函數__builtin_popcount()來計算數位1的個數)
#include<bits/stdc++.h> using namespace std; #define rep(i,n) for(int (i)= 0;i < (n);i++) #define MS1(a) memset(a,-1,sizeof(a)) typedef long long ll; ll dp[20][1<<10][11]; int bit[20],K; int newstate(int state,int v) { for(int i = v;i < 10;i++) if(state &(1 << i)) return (state ^ (1 << i))|(1<<v); return state|(1<<v); } ll dfs(int pos,int state,bool edge,bool zero) { if(pos == -1) return __builtin_popcount(state) == K; if(!edge && dp[pos][state][K] != -1) return dp[pos][state][K]; int end = edge ? bit[pos]:9; ll ans = 0; for(int i = 0;i <= end;i++){ ans += dfs(pos - 1,(zero && i == 0)?0:newstate(state,i),edge && i == end,zero && i == 0); } if(!edge) dp[pos][state][K] = ans; return ans; } ll calc(ll x) { int tot = 0; while(x){ bit[tot++] = x%10; x /= 10; } return dfs(tot - 1,0,1,1); } int main() { int T,kase = 1; MS1(dp); cin>>T; while(T--){ ll L,R; scanf("%I64d%I64d%d",&L,&R,&K); printf("Case #%d: %I64d\n",kase++,calc(R) - calc(L-1)); } } View Code