题目:点击打开链接
题意:求出给定区间内有多少数的最长严格单调子序列长度等于k
分析:因为这是严格递增的,所以长度不会大于9,考虑用二进制状压保存当前情况下的LIS,令dp[pos][num][K],num表示当前情况下按位保存0~9的出现情况(num<(1<<10)),对应位置出现该数存为1,未出现则为0,若当前数为1253,则num=0000101110,考虑到用复杂度为nlogn的LIS的思想,若对于当前的数,存在非末尾的某个数位大于等于最末尾的数位,则要把num的该数位改为0,把末尾数位的数的对应数位改为1,即上例中1253中5>3,则原来值125的num=0000100110要改为0000001110而不是0000101110。若不存在非末尾的某个数位大于等于最末尾的数位,则直接把末尾数位的数的对应数位改为1。这样就保证了每次取的都是最小的值,再统计num中有多少个1,也就能求出最长的长度了。注意排除前缀零的影响。
#pragma comment(linker, "/STACK:1024000000,1024000000") #include <stdio.h> #include <iostream> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #include <set> #include <vector> #include <map> #define sqr(x) ((x)*(x)) #define PR pair<int,int> #define MP make_pair #define fi first #define se second #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define ll long long const ll INF = 1e18; const int inf=0x3f3f3f3f; const int M=100010; const int N=15; const ll MOD=1000000007; const double eps=1e-8; const double pi=acos(-1.0); using namespace std; int dig[20],K; ll dp[20][1<<10][11],l,r; int getsta(int x,int s) { for(int i=x;i<10;i++) if(s&(1<<i)) return (s^(1<<i))|(1<<x); return s|(1<<x); } int getlis(int x) { int ans=0; while(x) { if(x&1) ans++; x>>=1; } return ans; } ll dfs(int pos,int num,int lim,int first) { if(pos==0) return getlis(num)==K; if(!lim&&dp[pos][num][K]!=-1) return dp[pos][num][K]; int upper=lim?dig[pos]:9; ll res=0; for(int i=0;i<=upper;i++) { if(i==0&&first) res+=dfs(pos-1,0,lim&&(i==upper),1); else res+=dfs(pos-1,getsta(i,num),lim&&(i==upper),0); } if(!lim) dp[pos][num][K]=res; return res; } ll calc(ll x) { int len=0; while(x) { dig[++len]=x%10; x/=10; } return dfs(len,0,1,1); } int main() { memset(dp,-1,sizeof(dp)); int T; scanf("%d",&T); for(int cas=1;cas<=T;cas++) { scanf("%I64d%I64d%d",&l,&r,&K); printf("Case #%d: %I64d\n",cas,calc(r)-calc(l-1)); } }