C - All Pair Digit Sums
題意:
設 \(f(x)\) 為 \(x\) 的數字和。例如 \(f(158)=1+5+8=14\)。
給定一個長度為 \(N\) 的正整數序列 \(A\),求 \(\sum_{i=1}^{N}\sum_{j=1}^{N}f(A_i+A_j)\)。
分析:
首先明確 \(f(x)\) 為 \(x\) 的數位和。
舉例情況:
若有兩個數分別為:\(12,21\)。
\[f(12+21)=f(12)+f(21)=3
\]
可以發現兩個數相加的數位和可以轉化為第一個數的數位和和第二個數數位和相加。
但總有特殊情況如:
這兩個數分別為:\(53,27\)。
\[f(53+27)=f(80)=8
\]
\[f(53)+f(27)=8+9=17
\]
可以發現這兩個數的數位相加時在個位上進了一位,就不符合上面的情況了,那該怎麼辦呢?
仔細思考進位對數位和的貢獻是什麼?
相當於此位變為 \(0\) 和更高的一位 \(+1\),則是對總答案相當於貢獻了 \(-9\)。
那麼我們就明確了計算的過程:
設 \(g(a,b)\) 表示 \(a+b\) 進位個數。
\[f(a+b)=f(a)+f(b)-9\times g(a,b)
\\
f(a)+f(b)=\sum_{i=1}^{N}\sum_{j=1}^{N}f(A_i)+f(A_j)=2N \times \sum_{i=1}^{N}f(A_i)
\\
9\times g(a,b)=9 \times \sum_{i=1}^{N}\sum_{j=1}^{N}g(A_i,A_j)
\]
第二個式子為什麼是 \(2n\) 呢?因為它作 \(a_i\) 時加了 \(n\) 次,它作 \(a_j\) 時被加了 \(n\) 次。
那怎麼求 \(g(a,b)\) 呢?
對於兩個數 \(a,b\),如果 \(a+b\) 在第 \(x\) 位上發生了進位,那麼有 \(a+b\ge10^x\)。
那麼我們就可以列舉 \(x\),按照每個前 \(x\) 位的數的大小排序,再列舉 \(j\),二分找到第一個 \(a_j+a_k\ge10^i\) 的數的下標 \(k\),\(k\) 後面的數就全是可以進位的,然後就可以 \((n-k+1)\) 求出 \(g(a,b)\) 的個數了。
時間複雜度為 \(O(n\log{n})\)。
下面見程式碼(有註釋不要擔心):
注:十年OI一場空,不開long long 見祖宗
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int n;
ll x;
ll a[20][200005];//a[前幾位的數][第幾個數]
ll ans=0;
ll p(ll x){//計算數位和
ll sum=0;
while(x){
sum+=x%10;
x/=10;
}
return sum;
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&x);
ans+=2*n*p(x);//先不考慮進位,單純地都加上
ll mod=10;
for(int y=1;y<=15;y++){
a[y][i]=x%mod;//前i的數為記錄起來
mod*=10;
}
}
ll w=1;
for(int i=1;i<=15;i++){//列舉前i位
w*=10;
sort(a[i]+1,a[i]+1+n);//對前i位的數的大小進行排序
for(int j=1;j<=n;j++){//對每個數進行排序
//統計加上a[i][j]後大於等於10^i的個數
ll k=n+1-(lower_bound(a[i]+1,a[i]+1+n,w-a[i][j])-a[i]);
ans-=9*k;//減去進位減少的貢獻
}
}
cout<<ans;
return 0;
}