1. 程式人生 > 實用技巧 >2020牛客暑期多校訓練營(第六場)H Harmony Pairs 題解

2020牛客暑期多校訓練營(第六場)H Harmony Pairs 題解

題意

設 S ( x ) 為x各數位之和,如 S (123) =1+2+3=6,求有多少數對(A,B)滿足S(A)>S(B) 0<=A<B<=N

N<=10^100。

看資料範圍,肯定是數位DP。

首先,我們考慮簡化問題:

當N=9999999……999999時答案時什麼樣的。

我們考慮分類討論:

當A、B位數不同時,我們設 f [ x ] [ y ] 為共x位 ( 算前導0 ),位數和為y的數字有多少個 。設sm[x][y]為f[x][y]的字尾和。

則我們可以列舉B的和,再利用sm[x][y]O(1)求解。

當A、B位數不同時,我們列舉A在哪一位首先小於B,然後分別列舉這一位之前 與之後的 A與B的位數和。求出A、B位數相同時的數對數。

兩者相加得到L[i],表示0~999……999( 共 i 個9)的答案。

接下來,我們正式開始數位DP

我們分三種情況DP:

當B DP到從高到低第X位時

1、A在前x-1位就已經確定它一定小於此時的B

設F [ x ] [ y ],為到當前第 x 位 時,A已經可以通過前x-1位判斷出A<B 且前x-1位數位和為y的方案數。

然後列舉B在這一位填什麼以及後面那些位的位數和來統計答案。

2、A前x-1位於B相同,在x拉開差距

我們列舉B與A在這一位的差值以及之後幾位裡B的數位和來求解

3、A與B前x為都相同且第x為小於A[i]

這裡就用我們之前求出的L[i]陣列乘上A[i]求解即可。

雖然思路看起來比較簡單,但是邊界還是很難處理的。詳情請看程式碼。

  1 #include<iostream>
  2 #include<cstdlib>
  3 #include<cstdio>
  4 #include<cstring>
  5 #include<algorithm>
  6 #include<cmath>
  7 #define N 105
  8 #define M 905
  9 using namespace std;
 10 const int p= 1e9+7;
 11 long long
f[N][M]; 12 long long sm[N][M]; 13 long long L[N]; 14 char bb[N]; 15 int A[N],n; 16 long long F[2][M]; 17 int main() 18 { 19 f[0][0]=1; 20 sm[0][0]=1; 21 for(int i=1;i<=100;i++) 22 { 23 for(int j=0;j<=(i-1)*9;j++) 24 { 25 for(int k=0;k<=9;k++) 26 { 27 f[i][j+k]+=f[i-1][j]; 28 f[i][j+k]%=p; 29 } 30 } 31 } 32 for(int i=1;i<=100;i++) 33 { 34 for(int j=900;j>=0;j--) 35 { 36 sm[i][j]=sm[i][j+1]+f[i][j]; 37 sm[i][j]%=p; 38 } 39 } 40 for(int i=1;i<=100;i++) 41 { 42 for(int j=1;j<=i;j++) 43 { 44 for(int k=j*9;k>=0;k--) 45 { 46 L[i]+=(f[j][k]-f[j-1][k]+p)%p*sm[j-1][k+1]%p; 47 L[i]%=p; 48 } 49 } 50 for(int j=1;j<=i;j++) 51 { 52 long long tmp1=sm[j-1][0]; 53 for(int k=1;k<=9;k++) 54 { 55 for(int l=0;l<=(i-j)*9;l++) 56 { 57 L[i]+=(tmp1-1)*f[i-j][l]%p*sm[i-j][l+k+1]%p*(9-k+1)%p; 58 L[i]%=p; 59 L[i]+=f[i-j][l]%p*sm[i-j][l+k+1]%p*(9-k)%p; 60 L[i]%=p; 61 } 62 63 } 64 65 } 66 } 67 scanf("%s",bb+1); 68 n=strlen(bb+1); 69 long long sum=0; 70 for(int i=1;i<=n;i++) 71 { 72 A[i]=bb[i]-'0'; 73 } 74 int now=n; 75 while(A[now]==9) 76 { 77 A[now]=0; 78 now--; 79 } 80 A[now]++; 81 if(now==0) 82 { 83 for(int i=n+1;i;i--) A[i]=A[i-1]; 84 A[0]=0; 85 n=n+1; 86 } 87 long long ans=0; 88 int la=1,nw=0; 89 for(int i=1;i<=n;i++) 90 { 91 int tmp=n-i; 92 for(int j=0;j<=(i-1)*9;j++) 93 { 94 if(!F[nw][j])continue; 95 for(int k=0;k<A[i];k++) 96 { 97 for(int l=0;l<=tmp*9;l++) 98 { 99 int tmpp=sum+k+l-j+1; 100 if(tmpp<0) tmpp=0; 101 if(tmpp>=0&&tmpp<=900) 102 { 103 ans+=F[nw][j]*f[tmp][l]%p*sm[tmp+1][tmpp]%p,ans%=p; 104 } 105 } 106 } 107 } 108 for(int j=0;j<=tmp*9;j++) 109 { 110 for(int k=1;k<=A[i]-1;k++) 111 { 112 ans+=f[tmp][j]*sm[tmp][j+k+1]%p*(A[i]-k)%p; 113 ans%=p; 114 } 115 } 116 ans+=1ll*L[tmp]*(A[i])%p; 117 ans%=p; 118 nw^=1,la^=1; 119 memset(F[nw],0,sizeof(F[nw])); 120 for(int j=0;j<=(i-1)*9;j++) 121 { 122 for(int k=0;k<=9;k++) 123 { 124 F[nw][j+k]+=F[la][j]; 125 F[nw][j+k]%=p; 126 } 127 } 128 for(int k=0;k<A[i];k++) 129 { 130 F[nw][sum+k]++; 131 } 132 sum+=A[i]; 133 } 134 printf("%lld\n",ans); 135 return 0; 136 } 137 /* 138 22 139 */
View Code