本题分为简单版本和困难版本,二者唯一的区别是:简单版本有序列 a 所有元素乘积 ≤1018的限制,困难版本没有。
氧气少年最近喜欢上了零。
给出一个长度为 n(1≤n≤2⋅105)的序列 a(1≤ai≤109),并且保证序列 a 所有元素乘积 ≤1018,求这个序列中满足如下条件的连续子段 [al…ar] 的数量:
- 令 x=al⋅al+1⋅al+2…ar,那么 xxx 的末尾恰好有 k(0≤k≤109) 个零。
输入描述:
第一行包含一个整数 T(1≤T≤105),表示测试用例的组数。 对于每组测试用例: 第一行包含两个整数 n(1≤n≤2⋅105) 和 k(0≤k≤109),表示序列的长度和题目中提到的后导零的数量; 第二行包含 n 个整数 a1…an (1≤ai≤109),表示该序列。 保证对于所有的测试用例,n 的总和不超过 2⋅105。
输出描述:
对于每组测试用例: 仅输出一行,包含一个整数,表示答案。
示例1
输入
2 5 3 125 1 8 1 1 1 0 6
输出
3 1
比赛的时候死活想不出来二分的来找,一直超时。。。。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[200001];
ll check(ll a)
{
ll res=0;
do{
if(a%10==0)
res++;
else
break;
a/=10;
}while(a);
return res;
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0);
ll t;
cin>>t;
while(t--)
{
ll n,k;
cin>>n>>k;
a[0]=1;
for(int i=1;i<=n;i++)
{
cin>>a[i];
a[i]*=a[i-1];
}
ll ans=0;
for(int i=1;i<=n;i++)
{
ll tl=i,tr=n;
while(tl<tr)
{
int mid=tl+tr>>1;
if(check(a[mid]/a[i-1])>=k) tr=mid;
else tl=mid+1;
}
ll ans2=tl;
tl=i,tr=n;
while(tl<tr)
{
ll mid=tl+tr+1>>1;
if(check(a[mid]/a[i-1])<=k) tl=mid;
else tr=mid-1;
}
if(check(a[ans2]/a[i-1])==k)
ans+=tl-ans2+1;
}
cout<<ans<<endl;
}
return 0;
}