1862. 向下取整数对和(暴力,调和级数的复杂度)
给你一个整数数组 nums
,请你返回所有下标对 0 <= i, j < nums.length
的 floor(nums[i] / nums[j])
结果之和。由于答案可能会很大,请你返回答案对109 + 7
取余 的结果。
函数 floor()
返回输入数字的整数部分。
示例 1:
输入:nums = [2,5,9] 输出:10 解释: floor(2 / 5) = floor(2 / 9) = floor(5 / 9) = 0 floor(2 / 2) = floor(5 / 5) = floor(9 / 9) = 1 floor(5 / 2) = 2 floor(9 / 2) = 4 floor(9 / 5) = 1 我们计算每一个数对商向下取整的结果并求和得到 10 。
示例 2:
输入:nums = [7,7,7,7,7,7,7] 输出:49
提示:
1 <= nums.length <= 1e5
1 <= nums[i] <= 1e5
这个题是只需要枚举他的倍数的,因为对于一个数i,枚举他的倍数j,那么这个倍数是j的数的范围为j*i----(j+1)*x-1,所以我们要想办法统计出来这个范围里面的
数的个数,这个是可以用前缀和来处理的,这里有一个小技巧,我没枚举的时候不能枚举这个序列的数,而是枚举从1到1e5.
下面看这两种写法
写法1:
for(int i=1;i<maxn;i++){ for(int j=1;j*i<maxn;j++){ int l=j*i,r=min(maxn-1,(j+1)*i-1); int s = (ll)(sum[r] - sum[l - 1]) * j % mod; ans = (ans + (ll)s * (sum[i] - sum[i - 1])) % mod;//这个数的个数 } }
写法2:
ll ans=0; for(auto x:nums){ for(int j=1;x*j<maxn;j++){ int l=j*x,r=min(maxn-1,(j+1)*x-1); int s=sum[r]-sum[l-1];//个数 ans+=(j*s)%mod; } }
我们提交一下会发现写法2是会被T掉的,为什么呢?
举个例子把,就是又1e5个1的话,做法1就会被T掉,而做法2就不会,自己好好想想把,
所以说这个一个小技巧吧,
const int maxn=1e5+100,mod=1e9+7; typedef long long ll; int sum[maxn]; class Solution { public: int sumOfFlooredPairs(vector<int>& nums) { memset(sum,0,sizeof(sum)); for(auto x: nums) sum[x]++; for(int i=1;i<maxn;i++){ sum[i]+=sum[i-1]; } ll ans=0; for(int i=1;i<maxn;i++){ for(int j=1;j*i<maxn;j++){ int l=j*i,r=min(maxn-1,(j+1)*i-1); int s = (ll)(sum[r] - sum[l - 1]) * j % mod; ans = (ans + (ll)s * (sum[i] - sum[i - 1])) % mod; } } return ans%mod; } };