首先考虑一下给一个数如何求它需要多少次操作。
显然用一个单调栈就可以完成:塞入栈中,将比它大的所有数都弹出,如果栈中没有当前数,答案+1。
因为数的范围只有0~9,所以我们可以用一个二进制数来模拟这个栈,并塞到DP的状态里。
设$dp[i][j][k]$表示前i位数,已经进行了j次操作,栈的状态为k的方案数。
每次枚举一个数的时候,先把比这个数大的数在状态中都清零,再看看状态中有没有这个数,没有的话答案+1。
注意需要把状态初始值设为0在栈中...T T
#include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #define ll long long using namespace std; ll l, r, K; ll dp[20][20][1<<10]; int a[20]; ll dfs(int pos, int k, int st, bool limit) { if(!pos) return k==K; if(!limit && dp[pos][k][st]!=-1) return dp[pos][k][st]; int up=limit?a[pos]:9; ll ans=0; for(int i=0;i<=up;i++) { int now=st; for(int j=i+1;j<=9;j++) now^=((now & (1<<j))!=0)<<j; if(st&(1<<i)) ans+=dfs(pos-1, k, now, limit && i==up); else if(k<K) ans+=dfs(pos-1, k+1, now|(1<<i), limit && i==up); } if(!limit) dp[pos][k][st]=ans; return ans; } ll solve(ll x) { int pos=0; while(x) a[++pos]=x%10, x/=10; return dfs(pos, 0, 1, 1); } int main() { memset(dp, -1, sizeof(dp)); scanf("%lld%lld%lld", &l, &r, &K); printf("%lld\n", solve(r)-solve(l-1)); }