题意:定义一个数的力量为这个数的最长严格上升子序列长, 找出区间内力量等于k的数,数据范围是long long型
比较明显的数位DP,重点在于怎么找这个最长严格上升子序列。用经典的dp可以做到nlgn求解最长子序列,dp[x]就是长度为x的子序列最后一位的最小值。实际上因为dp[x]是严格递增的,所以可以直接用状压来保存dp[x]中有哪些数,这个问题就解决了。剩下的就是数位dp套路了。
下面代码dp[full][now][mask][n]中,full为数位的上限标记,now为位数,mask为状压,n为题目中的k值
/* ***********************************************
Author :axp
Created Time :2016/3/15 18:43:27
TASK :B.cpp
LANG :C++
************************************************ */
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
using namespace std;
typedef long long ll;
const int N = 20;
int T;
int n;
int ar[N];
ll dp[2][N][1<<10][11];
ll f(bool full,int now,int mask);
inline void solve(bool full,int now,int mask,int x,ll &re)
{
if(mask==0 && x==0)
{
re+=f(full,now-1,mask);
return;
}
int t=1<<x;
if(mask<t)
mask|=t;
while((t&mask)==0)t<<=1;
mask-=t;
mask|=1<<x;
re+=f(full,now-1,mask);
}
ll f(bool full,int now,int mask)
{
ll &re=dp[full][now][mask][n];
if(full==0 && re!=-1)return re;
if(now==0)
{
int tot=0;
while(mask)
{
tot++;
mask-=mask&-mask;
}
re=(tot==n);
return re;
}
re=0;
int en=9;
if(full)
{
solve(1,now,mask,ar[now],re);
en=ar[now]-1;
}
for(int i=0;i<=en;i++)
solve(0,now,mask,i,re);
return re;
}
ll work(ll x)
{
int cnt=0;
//if(x==0)ar[++cnt]=0;
while(x)
{
ar[++cnt]=x%10;
x/=10;
}
ll re=f(1,cnt,0);
return re;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
memset(dp[0],-1,sizeof dp[0]);
scanf("%d",&T);
for(int kase=1;kase<=T;kase++)
{
ll l,r;
scanf("%I64d%I64d%d",&l,&r,&n);
ll ans=work(r)-work(l-1);
printf("Case #%d: %I64d\n",kase,ans);
}
return 0;
}