因为结尾0的个数取决于有多少对 ( 2 , 5 ) (2,5) (2,5) 相乘,那么对于下面例题,解题思路为:
1、将每个数字中的2和5剥离出来,然后用两个数组进行记录,然后其子数 ( 2 , 5 ) (2,5) (2,5)组成对至少为k个。
2、遍历每一个子数组然后计算:会发现超时:
下面公式推导一下:
t o t l e 2 − t w o > = k 、 t o t l e 5 − f i v e > = k 由于 t w o = f 2 [ j ] − f 2 [ i ] ; 这里的 f 为数组 2 剥离的前缀和 由于 f i v e = f 5 [ j ] − f 5 [ i ] ; 上述 i , j 为子数组 ( i , j − 1 ) 。因此我们固定 i ,由于前缀和是递增的,因此可以使用二分,找到右边界。 推到出: t o t l e − ( f [ j ] − f [ i ] ) > = k ; t o t l e + f [ i ] − k > = f [ j ] ; totle2-two>=k、totle5-five>=k\\ 由于two = f2[j]-f2[i];这里的f为数组2剥离的前缀和\\ 由于five = f5[j]-f5[i];\\ 上述i,j 为子数组(i,j-1)。因此我们固定i,由于前缀和是递增的,因此可以使用二分,找到右边界。\\ 推到出: totle-(f[j]-f[i])>=k;\\ totle+f[i]-k>=f[j]; totle2−two>=k、totle5−five>=k由于two=f2[j]−f2[i];这里的f为数组2剥离的前缀和由于five=f5[j]−f5[i];上述i,j为子数组(i,j−1)。因此我们固定i,由于前缀和是递增的,因此可以使用二分,找到右边界。推到出:totle−(f[j]−f[i])>=k;totle+f[i]−k>=f[j];
那么讨论一下边界问题:
二分 f [ j ] f[j] f[j]返回的是第一个大于 f [ j ] f[j] f[j]值的位置,但是这时就不符合 t o t l e + f [ i ] − k > = f [ j ] totle+f[i]-k>=f[j] totle+f[i]−k>=f[j]了,因此减去1,此时是刚好的。
#include <iostream>
#include <set>
#include <vector>
using namespace std;// 剥离n中存在几个base的因子。如果是2,和5的话,
// 就是base中存在多少个2相乘和多少个5相乘
int factor(int n, int base){int ans = 0;while (n and !(n%base)) {n/=base;ans++;}return ans;
}int base_right(const vector<int>& vec, int value){int left = 0;int right = vec.size()-1;int res = -1;while (left<=right) {int mid = left+((right-left)>>1);if(vec[mid]>value){res = mid;right = mid-1;}else{left = mid+1;}}if(res == -1) return vec.size();return res;
}int main() {int n, k;cin>>n>>k;vector<int> vec(n);vector<int> factor2(n+1);vector<int> factor5(n+1);int totle2 = 0;int totle5 = 0;for(int i=0;i<n;i++){cin>>vec[i];int x = factor(vec[i], 2);factor2[i+1] = factor2[i]+x;totle2+=x;x = factor(vec[i], 5);factor5[i+1] = factor5[i]+x;totle5+=x;}// 这里进行二分查找加速long long ans = 0;for(int i=0;i<n;i++){ // 以每个点为起点int val2 = totle2+factor2[i]-k;int val5 = totle5+factor5[i]-k;int pos2 = base_right(factor2, val2);int pos5 = base_right(factor5, val5);if(min(pos2, pos5)-i-1<=0) continue;ans+=min(pos2, pos5)-i-1;}cout<<ans<<endl;return 0;
}