题目链接:点击查看
题意:l - r 中有多少 最长上升序列为k的个数 不要求连续
题解:因为这个k并不大,所以我们可以想到用二进制来储存,我们再求一般的最长上升子序列时,是先保存在一个数组中,遇到一个数在替换数组中大于等于他的第一个数,如果没有,就直接加到后面,最后的数组长度即为最长上升子序列的长度,通过这个题来用二进制解决,0-9 我们用二进制的每一位来储存,遇到一个数x,在判断x-9有没有出现,是替换还是插入,剩下的就是数位dp的模板了,dp[len] [st] [k] 表示长度为len,当前状态为st,最长上升子序列为k的数目
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
ll dp[20][(1<<10)+10][12];
int wei[20];
int k;
int get_1(int x)
{
int cnt=0;
while(x)
{
if(x&1) cnt++;
x/=2;
}
return cnt;
}
int Change(int st,int x)
{
for(int i=x;i<=9;i++)
if((1<<i) & st)
return ((1<<i) ^ st) | (1<<x);
return st | (1<<x);
}
ll dfs(int pos,int st,int head0,int limit)
{
if(pos<0)
{
return get_1(st)==k;
}
if(!limit&&dp[pos][st][k]!=-1) return dp[pos][st][k];
int now=limit?wei[pos]:9;
ll ans=0;
for(int i=0;i<=now;i++)
{
if(i==0&&head0)
{
ans+=dfs(pos-1,st,head0,limit&&i==now);
}
else
{
ans+=dfs(pos-1,Change(st,i),0,limit&&i==now);
}
}
return limit==0? dp[pos][st][k]=ans:ans;
}
ll solve(ll x)
{
int len=0;
while(x)
{
wei[len++]=x%10;
x/=10;
}
return dfs(len-1,0,1,1);
}
int main()
{
int T;
int nn=1;
ll l,r;
memset(dp,-1,sizeof(dp));
scanf("%d",&T);
while(T--)
{
scanf("%lld%lld%d",&l,&r,&k);
printf("Case #%d: %lld\n",nn++,solve(r)-solve(l-1));
}
return 0;
}