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;
}
标签:题解,ARC158C,long,times,sum,Pair,ll,进位,数位
From: https://www.cnblogs.com/sadlin/p/18536828