题意:定义函数 S(x) S ( x ) 是把 x x 各数位排序后得到的数,求
S(2018)=128,S(998244353)=233445899 S ( 2018 ) = 128 , S ( 998244353 ) = 233445899
n≤10700 n ≤ 10 700
看着 n n 这么大显然是数位了
扒一下官方题解
This is a digit dp d p problem. Let’s try to solve the subproblem “How many ways can the i−th i − t h digit be at least j j ?”. Let’s fix , and solve this with dp d p . We have a dp state dp[a][b][c] d p [ a ] [ b ] [ c ] =number of ways given we’ve considered the first a digits of X X , we need more occurrences of digits at least j j , and is a boolean saying whether or not we are strictly less than X X or not yet.
For a fixed digit, we can compute this dp table in time, and then compute the answers to our subproblem for each i i ( by varying b b in our table).
1. ⇒ ⇒ in other words/this is
考虑到原问题很难做,我们转换一下问题
举个例子:假设第 i i 位填,那么他的贡献的 3∗10i 3 ∗ 10 i ,考虑把这个贡献拆开
3≥3,3≥2,3≥1 3 ≥ 3 , 3 ≥ 2 , 3 ≥ 1 我们在这一位填 1,2,3 1 , 2 , 3 时都记上 10i 10 i 的贡献
那么我们就只要记这一位填大于等于某个数的方案数就好了
考虑按照套路设
dp[i][j][k][l]
d
p
[
i
]
[
j
]
[
k
]
[
l
]
表示前
i
i
位有位的数字大小大于等于
k
k
,是否严格小于的方案数
枚举第 i+1 i + 1 位填 p p
然后实际上假设前 n n 位我们位数字大于等于 k k 的方案数是
这个对答案的贡献的是 sum∗111…11j个1 s u m ∗ 111 … 11 ⏟ j 个 1
#include<bits/stdc++.h>
#define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
#define go(u) for(register int i=fi[u],v=e[i].to;i;v=e[i=e[i].nx].to)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
using namespace std;
const int N=705,P=1e9+7;
int n,ans,a[N],f[N][N][10][2];char s[N];
inline void add(int&a,int b){a+=b,a>=P?a-=P:0;}
int main(){
#ifndef ONLINE_JUDGE
file("s");
#endif
scanf("%s",s+1);n=strlen(s+1);
fp(i,1,n)a[i]=s[i]-48;
fp(i,0,9)f[0][0][i][0]=1;
fp(i,0,n-1)fp(j,0,i)fp(k,1,9)fp(l,0,1)fp(p,0,(l?9:a[i+1]))
add(f[i+1][j+(p>=k)][k][l|(p<a[i+1])],f[i][j][k][l]);
fp(k,1,9){
int tp=1;
fp(i,1,n)add(ans,1ll*tp*(f[n][i][k][0]+f[n][i][k][1])%P),tp=(10ll*tp+1)%P;
}printf("%d",ans);
return 0;
}
发现这个转移是可以滚动的,而且每一个 k k <script type="math/tex" id="MathJax-Element-14892">k</script>还可以分开考虑贡献
所以泥萌可以自己再卡一卡常数