题意:定义函数S(x)S(x)是把xx各数位排序后得到的数,求
S(2018)=128,S(998244353)=233445899S(2018)=128,S(998244353)=233445899
n≤10700n≤10700
看着nn这么大显然是数位了
扒一下官方题解
This is a digit dpdp problem. Let’s try to solve the subproblem “How many ways can thei−thi−th digit be at least jj?”. Let’s fix , and solve this with dpdp. We have a dp state dp[a][b][c]dp[a][b][c]=number of ways given we’ve considered the first a digits of XX, we need more occurrences of digits at least jj, and is a boolean saying whether or not we are strictly less than XX 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 ii ( by varying bb in our table).
1. ⇒⇒in other words/this is
考虑到原问题很难做,我们转换一下问题
举个例子:假设第ii位填,那么他的贡献的3∗10i3∗10i,考虑把这个贡献拆开
3≥3,3≥2,3≥13≥3,3≥2,3≥1我们在这一位填1,2,31,2,3时都记上10i10i的贡献
那么我们就只要记这一位填大于等于某个数的方案数就好了
考虑按照套路设dp[i][j][k][l]dp[i][j][k][l]表示前ii位有位的数字大小大于等于kk,是否严格小于的方案数
枚举第i+1i+1位填pp
然后实际上假设前nn位我们位数字大于等于kk的方案数是
这个对答案的贡献的是sum∗111…11j个1sum∗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;
}
发现这个转移是可以滚动的,而且每一个kk还可以分开考虑贡献
所以泥萌可以自己再卡一卡常数