小美的区间删除

因为结尾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]; totle2two>=ktotle5five>=k由于two=f2[j]f2[i];这里的f为数组2剥离的前缀和由于five=f5[j]f5[i];上述i,j为子数组(i,j1)。因此我们固定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;
}
  • 15
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值