CF908G New Year and Original Order

题意

给你一个数(n),另(S(x))表示(x)中各位数从小到大排序后的数,例如(S(120542)=12245)
(sum_{i=1}^n S(i))

(1 le n le 10^{700})

传送门

思路

首先肯定是一道数位dp

考虑将某位数的贡献(x imes 10^i)转化为(x)(10^i)相加,设(dp[i][j][k][0/1])表示前(i)位中有(j)位的数字大于等于(k),是否等于(n)的方案数。

转移很显然(dp[i][j+(now ge k)][k][h &(now==n[i])]+=dp[i-1][j][k][h])

考虑计算答案,对于(dp[n][i][j][0/1])(j)位的数都会产生贡献,加起来就是(underbrace{111cdots11}_{j ext{个}1})。那么对于某个数(x),当(1le i le x)的时候都会计算到一次,所以累计就是答案。

#include <bits/stdc++.h>
const int mu=1e9+7;
int dp[705][705][10][2],ans;
char s[705];
void reduce(int &x) { x+=x>>31&mu; }
int main(){
	scanf("%s",s);
	int l=strlen(s);
	for (int i=1;i<=9;i++) dp[0][0][i][1]=1;
	for (int i=1;i<=l;i++)
		for (int j=0;j<i;j++)
			for (int k=1;k<=9;k++)
				for (int h=0;h<=1;h++){
					int t=dp[i-1][j][k][h];
					if (!t) continue;
					for (int now=0;now<=9;now++){
						if(h==1 && now>s[i-1]-'0') break;
						reduce(dp[i][j+(now>=k)][k][h&(now==s[i-1]-'0')]+=t-mu);
					}		
				}
	for (int i=1;i<=9;i++){
		for (int j=1,t=1;j<=l;j++,t=(t*10ll+1)%mu)
			ans=(ans+1ll*t*(dp[l][j][i][0]+dp[l][j][i][1]))%mu;
	}
	printf("%d
",ans);
}
原文地址:https://www.cnblogs.com/flyfeather6/p/13226864.html