1. 差分转化
直接统计“恰好等于 ”较为困难,因为
是一个非单调的约束。依据排除法原理,我们可以将约束转化为:
令
为满足
的子段数量。
条件
等价于:
这是一个具有二元单调性的判定问题,非常适合双指针或滑动窗口优化。
2. 双指针 + 滑动窗口
对于确定的左端点 ,随着右端点
的增加,
和
同步增加。
- 如果我们找到了最小的
使得
且
,那么对于所有
,该条件均成立。
- 此时,以
为左端点且满足条件的子段数量为
。
- 利用双指针,左端点
主动右移,右端点
根据条件联动右移,复杂度可控制在
。
3. 复杂度分析
- 时间复杂度:
- 因子提取:对于每个元素
,通过循环除法提取因子,复杂度为
。
- 双指针扫描:计算
和
分别需要一次线性扫描,复杂度为
。
- 总体:
。
- 因子提取:对于每个元素
- 空间复杂度:
- 需要存储每个元素的
和
计数,空间复杂度为
。
- 需要存储每个元素的
4. 代码实现
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
auto decomposition = [](int factor, ll v, vector<ll>& cnt,
int idx) -> void {
ll c = 0;
while (v % factor == 0) {
c++;
v /= factor;
}
cnt[idx] = c;
};
int T;
cin >> T;
while (T--) {
int n;
ll k;
cin >> n >> k;
vector<ll> cnt2(n), cnt5(n);
for (int i = 0; i < n; i++) {
ll a;
cin >> a;
decomposition(2, a, cnt2, i);
decomposition(5, a, cnt5, i);
}
auto count_at_least = [&](ll k) -> ll {
if (k == 0) {
return (ll)n * (n + 1) / 2;
}
ll res = 0;
int r = 0;
ll sum2 = 0;
ll sum5 = 0;
for (int l = 0; l < n; l++) {
while (r < n && (sum2 < k || sum5 < k)) {
sum2 += cnt2[r];
sum5 += cnt5[r];
r++;
}
if (sum2 >= k && sum5 >= k) {
res += (n - r + 1);
}
sum2 -= cnt2[l];
sum5 -= cnt5[l];
}
return res;
};
ll ans = count_at_least(k) - count_at_least(k + 1);
cout << ans << "\n";
}
}

京公网安备 11010502036488号