【CF908G】New Year and Original Order 数位DP
【CF908G】New Year and Original Order
题意:令S(i)表示将i中所有数位上的数拿出来,从小到大排序后组成一个新的数的值。如S(50394)=3459。求$\sum\limits_{i=1}^nS(i)$。
$n\le 10^{700}$。
题解:比较难的数位DP。我们考虑分别计算每个数字的贡献。令f0[i][a][b]表示考虑到第i位数,其中数字a的最高为是b的数的数量,再令f1[i][a][b]表示a这个数的贡献。再设g0,g1表示小于等于n的所有数的DP值。转移比较复杂,我能1A也是不容易啊。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; typedef long long ll; const ll P=1000000007; int n; int v[705]; ll ans; ll f0[705][10][705],f1[705][10][705],g0[705][10][705],g1[705][10][705],pw[705]; char str[705]; int main() { int i,a,b,j; scanf("%s",str),n=strlen(str); for(pw[0]=i=1;i<=n;i++) v[i]=str[n-i]-'0',pw[i]=pw[i-1]*10%P; for(a=0;a<=9;a++) f0[0][a][0]=g0[0][a][0]=1; for(i=1;i<=n;i++) for(a=0;a<=9;a++) for(b=0;b<=9;b++) { if(b==8) { b++,b--; } if(a<b) for(j=0;j<i;j++) { f1[i][b][j]=(f1[i][b][j]+f1[i-1][b][j])%P; f0[i][b][j]=(f0[i][b][j]+f0[i-1][b][j])%P; if(a<v[i]) { g1[i][b][j]=(g1[i][b][j]+f1[i-1][b][j])%P; g0[i][b][j]=(g0[i][b][j]+f0[i-1][b][j])%P; } else if(a==v[i]) { g1[i][b][j]=(g1[i][b][j]+g1[i-1][b][j])%P; g0[i][b][j]=(g0[i][b][j]+g0[i-1][b][j])%P; } } else if(a==b) for(j=0;j<i;j++) { f1[i][b][j+1]=(f1[i][b][j+1]+f1[i-1][b][j]+f0[i-1][b][j]*a*pw[j])%P; f0[i][b][j+1]=(f0[i][b][j+1]+f0[i-1][b][j])%P; if(a<v[i]) { g1[i][b][j+1]=(g1[i][b][j+1]+f1[i-1][b][j]+f0[i-1][b][j]*a*pw[j])%P; g0[i][b][j+1]=(g0[i][b][j+1]+f0[i-1][b][j])%P; } else if(a==v[i]) { g1[i][b][j+1]=(g1[i][b][j+1]+g1[i-1][b][j]+g0[i-1][b][j]*a*pw[j])%P; g0[i][b][j+1]=(g0[i][b][j+1]+g0[i-1][b][j])%P; } } else for(j=0;j<i;j++) { f1[i][b][j+1]=(f1[i][b][j+1]+f1[i-1][b][j]*10)%P; f0[i][b][j+1]=(f0[i][b][j+1]+f0[i-1][b][j])%P; if(a<v[i]) { g1[i][b][j+1]=(g1[i][b][j+1]+f1[i-1][b][j]*10)%P; g0[i][b][j+1]=(g0[i][b][j+1]+f0[i-1][b][j])%P; } else if(a==v[i]) { g1[i][b][j+1]=(g1[i][b][j+1]+g1[i-1][b][j]*10)%P; g0[i][b][j+1]=(g0[i][b][j+1]+g0[i-1][b][j])%P; } } } for(a=0;a<=9;a++) for(i=1;i<=n;i++) ans=(ans+g1[n][a][i])%P; printf("%lld",ans); return 0; }
| 欢迎来原网站坐坐! >原文链接<