题意:求[l,r]区间里数字满足最长上升子序列为k的个数。
思路:数位dp
首先需要将含有那些数字转化成二进制状态,对于一个新的数位,利用nlogn的思路对状态进行更新,但是需要注意,需要判前导0,因为只有没有前导0的才是数字。
要点就是n*logn状态的写法,其实它的思想就是将最后一位尽可能的小,看代码应该可以理解。
思路:
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
#include <map>
#include <vector>
#include <set>
#include <string>
#include <math.h>
#include <stack>
typedef long long ll;
#define INF 0x3f3f3f3f
const int maxn=3e5+10;
const int MAXN=1e3+10;
using namespace std;
int a[30];
ll dp[30][1<<10][12];
int k;
int getnum(int x)
{
int ans=0;
while(x)
{
if(x&1)
{
ans++;
}
x>>=1;
}
return ans;
}
int getnew(int x,int s)//更新新的状态
{
for(int i=x;i<10;i++)
if(s&(1<<i))return (s^(1<<i))|(1<<x);//如果有比x大的数位,那么就用x来进行替换
return s|(1<<x);
}
ll dfs(int pos,int sta,bool limit,bool ze)
{
if(pos==-1)
{
return k==getnum(sta);
}
if(!limit&&dp[pos][sta][k]!=-1)
{
return dp[pos][sta][k];
}
int up=limit?a[pos]:9;
ll ans=0;
for(int i=0;i<=up;i++)
{
ans+=dfs(pos-1,(ze&&(i==0))?0:getnew(i,sta),limit&&i==up,ze&&(i==0));
}
if(limit==0)
{
dp[pos][sta][k]=ans;
}
return ans;
}
ll solve(ll x)
{
int pos=0;
while(x)
{
a[pos++]=x%10;
x/=10;
}
return dfs(pos-1,0,1,1);
}
int main(int argc, char const *argv[])
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
int T;
cin>>T;
int Case=0;
memset(dp,-1,sizeof(dp));
while(T--)
{
ll l,r;
//scanf("%lld%lld%d",&l,&r,&k);
cin>>l>>r>>k;
printf("Case #%d: ",++Case);
cout<<solve(r)-solve(l-1)<<endl;
}
return 0;
}