题意:给出一个区间,求这个区间内把数字看成字符串后最长上升子序列(可以不连续)为k的个数。
思路:可以用类似求lis的方法来做,因为k最大只有10,而且lis中的g数组是递增的,g的值只有可能取0到9,所以我们可以用状态压缩表示当前g数组中0到9是否出现,然后在数位dp的时候计算状态即可。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#include<ctime>
#define eps 1e-6
#define LL long long
#define pii pair<int, int>
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
//const int MAXN = 5000000 + 5;
//const int INF = 0x3f3f3f3f;
int n, digit[20], k;
LL dp[20][20][1<<10];
int getNum(int s) {
int ans = 0;
for(int i = 0; i < 10; i++)
if((1<<i) & s) ans++;
return ans;
}
int update(int x, int s) {
for(int i = x; i < 10; i++)
if((1<<i)&s) return s^(1<<i)|(1<<x);
return s|(1<<x);
}
LL dfs(int len, int s, bool same, bool top) {
if(!len) return getNum(s) == k;
if(!same && !top && dp[k][len][s]!=-1) return dp[k][len][s];
int maxd = same ? digit[len] : 9;
LL ans = 0;
for(int i = 0; i <= maxd; i++) {
if(top && !i) ans += dfs(len-1, s, 0, 1);
else ans += dfs(len-1, update(i, s), same&&i==maxd, 0);
}
if(!same && !top) dp[k][len][s] = ans;
return ans;
}
LL solve(LL num) {
int len = 0;
while(num) {
digit[++len] = num % 10;
num /= 10;
}
return dfs(len, 0, 1, 1);
}
int main() {
//freopen("input.txt", "r", stdin);
memset(dp, -1, sizeof(dp));
int T, kase = 0;
cin >> T;
while(T--) {
LL le, ri;
scanf("%I64d%I64d%d", &le, &ri, &k);
LL ans = solve(ri) - solve(le-1);
printf("Case #%d: %I64d\n", ++kase, ans);
}
return 0;
}