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);
	}
}
posted @ 2022-03-17 20:43  Prean  阅读(125)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};