给出一个长度为n(1≤n≤2.105)的序列a(1≤ai≤109),并且保证序列a所有元素乘积≤1018,求这个序列中满足如下条件的连续子段[at...ar]的数量: •令x=a{:a1+1·a1+2...ar,那么x的末尾恰好有k(0≤k≤109)个零。给出c++代码
时间: 2024-02-11 16:07:58 浏览: 116
C++版本计算n阶乘末尾0的个数原理讲解及代码实现
好的,以下是实现了上述算法的C++代码:
```c++
#include <iostream>
#include <map>
#include <vector>
using namespace std;
const int MAXN = 2e5 + 5;
int n, k;
long long a[MAXN], p[MAXN];
map<int, int> cnt;
int main() {
cin >> n >> k;
for(int i = 1; i <= n; i++) {
cin >> a[i];
p[i] = p[i - 1] + a[i];
}
long long ans = 0;
cnt[0] = 1;
for(int r = 1; r <= n; r++) {
int t = p[r] % 10;
if(t == 0) ans++; // 特判末尾为0的情况
while(t % 2 == 0) t /= 2;
while(t % 5 == 0) {
t /= 5;
cnt[0]++;
}
for(int i = 1; i <= k; i++) {
if(i <= cnt.size() - 1 && cnt[i] > 0) {
ans += cnt[i];
}
if(cnt.find(i) == cnt.end()) {
cnt[i] = 0;
}
}
if(cnt.find(k) != cnt.end() && cnt[k] > 0) {
int l = 1, r = cnt[k - 1];
while(l <= r) {
int mid = (l + r) / 2;
if(cnt[k] - cnt[k - 1] + cnt[k - mid - 1] >= 1) {
r = mid - 1;
} else {
l = mid + 1;
}
}
ans += l;
}
cnt[t]++;
}
cout << ans << endl;
return 0;
}
```
其中,cnt数组维护了左端点l到当前右端点r中质因子分解后包含i个质因子5的数的个数。在这个代码中,我们使用了一个map来实现cnt数组,这样可以避免数组空间过大的问题。
在代码中,我们对每个右端点r维护cnt数组,然后利用二分找到满足条件的最小左端点l。具体来说,我们先用二分找到满足条件的最小k,然后从k向0遍历,每次将cnt[k]加到cnt[k-1]中,表示当前k个5的数都可以向左扩展一位,然后再用二分找到满足cnt[k-1]≥1的最小的l即可。
时间复杂度为O(nlognlogm),其中m是序列中最大的数的质因子个数。
阅读全文