CF908G&LOJ6697题解
为什么我从ACAM做到了数位DP啊
考虑枚举顶着最大值的前缀和没有顶着最大值的后缀。
考虑计算一个数对答案的贡献。统计 \(t\) 的出现次数记录到 \(c[t]\) 中。
贡献就是 \(\sum_{i=0}^{9}((\sum_{x=0}^{\sum_{j=i}^{9}c[j]-1}i\times10^{x})-(\sum_{x=0}^{\sum_{j=i+1}^{9}c[j]-1}i\times 10^{x}))\)。
\[\sum_{i=0}^{9}(i\times\sum_{x=0}^{\sum_{j=i}^{9}c[j]-1}10^{x})-(i\times\sum_{x=0}^{\sum_{j=i+1}^{9}c[j]-1}10^{x})
\]
\[((\sum_{i=0}^{9}i\times(10^{\sum_{j=i}^{9}c[j]}-1))-(\sum_{i=0}^{9}i\times(10^{\sum_{j=i+1}^{9}c[j]}-1)))\div 9
\]
\[((\sum_{i=0}^{9}i\times(10^{\sum_{j=i}^{9}c[j]}-1))-(\sum_{i=1}^{9}(i-1)\times(10^{\sum_{j=i}^{9}c[j]}-1)))\div 9
\]
\[\sum_{i=1}^{9}10^{\sum_{j=i}^{9}c[j]}-1\div 9
\]
考虑计算每一个数码对答案的贡献。
设 \(dp[t][n][m]\) 为只有 \(n\) 位(考虑前导 \(0\))并且 \(\sum_{i=t}^{9}c[i]=m\) 的数的个数。
对于长度为 \(k\) 的前缀以及对应的 \(c\) 数组,答案为:
\[\sum_{t=1}^{9}\sum_{i=1}^{n-k}dp[t][n-k][i]\times(10^{i+c[t]}-1)\div 9
\]
正确性的话先考虑 \(k=0\),然后再考虑这个就行了。
具体转移的话,有:\(dp[t][n][m]=dp[t+1][n][m]+\sum_{c=0}^{9}dp[t][n-1][m-[t\leq c]]\)。
边界条件的话,大概是 \(dp[c][0][0]=1\)。
#include<algorithm>
#include<cstring>
#include<cstdio>
typedef unsigned ui;
const ui M=705,mod=1e9+7;
ui n,c[10],S[M],pw10[M],dp[11][M][M];ui a[M];char s[M];
inline ui Solve(ui*c,const ui&n){
static ui t[10];
ui ans(0);t[9]=c[9];
for(ui i=9;i<10;--i)t[i]=t[i+1]+c[i];
for(ui c=1;c<10;++c){
for(ui i=0;i<=n;++i){
ans=(ans+1ull*dp[c][n][i]*(pw10[i+t[c]]-1))%mod;
}
}
return ans;
}
signed main(){
ui ans(0);pw10[0]=1;
scanf("%s",s+1);n=strlen(s+1);
for(ui i=1;i<=n;++i)a[i]=s[i]-48;
for(ui i=1;i<=n;++i)pw10[i]=10ull*pw10[i-1]%mod;
for(ui c=0;c<10;++c)dp[c][0][0]=1;
for(ui c=9;c<10;--c){
for(ui i=1;i<=n;++i)for(ui j=0;j<=i;++j){
for(ui t=0;t<10;++t)if(!(!j&&c<=t))dp[c][i][j]=(dp[c][i][j]+dp[c][i-1][j-(c<=t)])%mod;
}
}
for(ui i=1;i<=n;++i){
for(ui j=0;j<a[i];++j){
++c[j];ans=(ans+Solve(c,n-i))%mod;--c[j];
}
++c[a[i]];
}
std::sort(a+1,a+n+1);ans=1ull*ans*111111112%mod;
for(ui i=1;i<=n;++i)ans=(ans+1ull*pw10[n-i]*a[i])%mod;
printf("%u",ans);
}
接下来考虑乘上了权值应该怎么做。
考虑每次枚举的后缀集合是 \(P\),前缀部分连接起来的权值为 \(k\),那么我们应该计算的是 \(\sum_{g\in P}(k+g)\times f(k+g)=k\times\sum_{g\in P}f(k+g)+\sum_{g\in P}g\times f(k+g)\)。
仍然考虑那个 DP。设 \(f[t][n][m]\) 表示 \(n\) 位数中 \(\sum_{i=t}^{B-1}c[i]=m\) 的数字的和,\(g[t][n][m]\) 表示满足上述条件的数的个数。
转移和上面没什么区别,带上权值就行了。
\[dp[t][n][m]=dp[t+1][n][m]+\sum_{c=0}^{9}dp[t][n-1][m-[t\leq c]]+c\times B^{n-1}\times g[t][n-1][m-[t\leq c]]
\]
#include<algorithm>
#include<cstring>
#include<cstdio>
typedef unsigned ui;
const ui M=1005,mod=998244353;
ui n,T,B,invb,c[10],pw[M],f[11][M][M],g[11][M][M];ui a[M];char s[M];
ui top,pri[M],pos[M];
inline ui pow(ui a,ui b=mod-2){
ui ans(1);for(;b;b>>=1,a=1ull*a*a%mod)if(b&1)ans=1ull*ans*a%mod;return ans;
}
inline ui Solve(ui*c,const ui&n,const ui&C){
static ui t[10];
ui ans(0);t[B-1]=c[B-1];
for(ui i=B-1;i<B;--i)t[i]=t[i+1]+c[i];
for(ui c=1;c<B;++c){
for(ui i=0;i<=n;++i){
ans=(ans+(f[c][n][i]+1ull*C*g[c][n][i])%mod*(pw[i+t[c]]-1))%mod;
}
}
return ans;
}
signed main(){
scanf("%u%u",&T,&B);pw[0]=1;invb=pow(B-1);
for(ui i=1;i<=1000;++i)pw[i]=1ull*B*pw[i-1]%mod;
for(ui c=0;c<B;++c)g[c][0][0]=1;
for(ui c=B-1;c<B;--c){
for(ui i=1;i<=1000;++i)for(ui j=0;j<=i;++j){
for(ui t=0;t<B;++t)if(!(!j&&c<=t)){
g[c][i][j]=(g[c][i][j]+g[c][i-1][j-(c<=t)])%mod;
f[c][i][j]=(f[c][i][j]+f[c][i-1][j-(c<=t)]+1ull*t*pw[i-1]%mod*g[c][i-1][j-(c<=t)])%mod;
}
}
}
while(T--){
ui C(0),sum(0),ans(0);
scanf("%s",s+1);n=strlen(s+1);
for(ui i=1;i<=n;++i)a[i]=s[i]-48;
for(ui i=1;i<=n;++i){
for(ui j=0;j<a[i];++j){
++c[j];ans=(ans+Solve(c,n-i,(sum+1ull*j*pw[n-i])%mod))%mod;--c[j];
}
++c[a[i]];sum=(sum+1ull*a[i]*pw[n-i])%mod;
}
for(ui i=1;i<=n;++i)C=(C+1ull*pw[n-i]*a[i])%mod;
std::sort(a+1,a+n+1);ans=1ull*ans*invb%mod;
for(ui i=1;i<=n;++i)ans=(ans+1ull*pw[n-i]*a[i]%mod*C)%mod,a[i]=s[i]=0;
for(ui i=0;i<B;++i)c[i]=0;
printf("%u\n",ans);
}
}