题意:

求出a[i]后面的数+a[i]与a[i]的位数差的和

思路:

  1. 可以整体二分,然后分治。假设这个数组长度为n,把它分成两半,就是[1,mid]和[mid+1,n],我们可以先求出[1,mid]和[mid+1,n]各自的位数差,然后求出[1,mid]里的数作为a[i],[mid+1,n]里的数作为a[j]这样一个区间的位数差。
  • 举个例子:数组为4 100 19 20 3999,那么会先分成[4,100,19]和[20,3999],先算出左边位数差,4和100,4和19,100和19,然后右边20和3999,那么还得求4、100、19分别和20、3999的位数差。
  1. 判断一个数与另一个数的位数差:开一个数组分别存放2位数到9位数中最小的那个数.对于a[i],只要大于这个数,说明位数取小了,下标+1,否则就可以求出最小的满足位数差1的值。有了这个值(不一定在数组里存在),那么比它大的值肯定都能满足,所以可以对数组的[mid+1,n]升序排序,找这个值的位置用二分查找,找到>=这个值的第一个位置,然后就能知道这里有多少个数位数比它大1了。注意这里不是直接加上它们的位数差,而是这些数的个数,因为是先找到a[j]比a[i]位数差>=1的数,然后是差>=2.....一直到差>=8(如果存在的话),每次加的是这些数的个数,其实这么加也就是答案,假设a[j]+a[i]比[i]大8位,那么位数差>=1的时候会加一次,>=2会加一次....一直到8总共加8次。

代码:

#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const int N=100100;
int a[N];
ll b[10]={10,100,1000,10000,100000,1000000,10000000,100000000,1000000000};
int n;

ll solve(int l,int r)
{
	if(l>=r) return 0;
	int mid=l+r>>1;
	ll res=solve(l,mid)+solve(mid+1,r);
	sort(a+mid+1,a+r+1);
	for(int i=l;i<=mid;i++)
	{
		for(int j=0;j<9;j++)
		{
			if(b[j]-a[i]>0)
			{
				ll x=b[j]-a[i];
				res+=a+r-lower_bound(a+mid+1,a+r+1,x)+1;
			}
		}
	}
	return res;
}


int main() {
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    printf("%lld\n",solve(1,n));
    
	return 0;
}