这题给出了一个全新的做题方法:前缀积(说来也是,有前缀和那肯定也有前缀积啊)
首先读入后构建前缀积,然后把 1 1 1 ~ n n n 作为左端点,然后二分去搜靠左的右端点和靠右的右端点,这里的意义就是避免有连着的相同的积,可以构建出多个区间,比如样例中的:125 1 8 1 1
,连续的1就可以导致这种局面。
搜完之后,再判断一下是不是能搜到一个答案,如果能搜到就加上靠右的右端点减去靠左的右端点加1.
#include<iostream>
using namespace std;
#define ll long long
const int N = 2e5 + 10;
ll a[N]; //开longlong存前缀积
int n, k;
int check(ll x) { //返回后导0数量的函数
ll res = 0;
while (x % 10 == 0 && x) { //注意两个判别条件
x /= 10; //1.x的最后一位是0 2.x没有变成0
res++;
}
return res;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int t; cin >> t;
while (t--) {
cin >> n >> k;
a[0] = 1; //a[0]设为1,便于建立前缀积
for (int i = 1; i <= n; i++) {
cin >> a[i];
a[i] *= a[i - 1]; //读入并建立前缀积
}
ll ans = 0;//这里要开long long
for (int i = 1; i <= n; i++) { //枚举所有左端点
int l = i, r = n;
while (l < r) { //二分搜靠左的右端点
int mid = (ll)l + r >> 1;
if (check(a[mid] / a[i - 1]) >= k)r = mid;
else l = mid + 1;
}
ll res = l; //记录上端点
l = i, r = n;
while (l < r) { //二分搜靠右的右端点
int mid = (ll)l + r + 1 >> 1;
if (check(a[mid] / a[i - 1]) <= k)l = mid;
else r = mid - 1;
}
//如果i~右端点对应的前缀积后导0的个数是等于k的
if (check(a[res] / a[i - 1]) == k) //这里要判断,因为可能二分到最后没有答案
ans += l - res + 1; //答案加上两点相减
//这里因为搜到的左和右两个端点到i的积是一样的,搜左右的意义就在于搜到那种连续相同积的情况
}
cout << ans << endl;
}
return 0;
}