题意:
给一个数列,求出子区间个数,要求满足
数据范围:
1<=n<=2e5
1<=K<=1e5
1<=ai<=1e8
题解:
数据范围已经安排好了算法。计算等式右端的最大值:1e5*2e5*1e8=2e18。嗯LL刚好。
假如说有ai>=2,那么直接暴力即可,因为左端是一个乘积,最多乘log2e18个数字就比右端大了。复杂度64n左右。
那么有连续的ai=1,则可以将他们并作一段,观察等式左边增加一个1,值不变,右边增加一个1,值增大K。于是对于连续的一段1,可以直接O1做一次check,其他情况依旧暴力。只是常数大了一倍左右。
Attention:
判断 a * b 是否溢出:
inline bool overflow_LL(LL a,LL b){
return a>LLONG_MAX/b;
}
//C++14不可用,a*b/b会被优化成a,C++11/17的O(1-3)均不会进行优化。
inline bool overflow_LL(LL a,LL b){
return a*b/b!=a;
}
//通过volatile拒绝编译器优化,可以避免上面的问题
inline bool overflow_LL(LL a,LL b){
volatile LL c = a*b;
return c/b!=a;
}
不信的话。。可以尝试一下第二种溢出方式。。。然后你会认识132号数据点。
Code:
//
// Created by calabash_boy on 18-6-19.
//
#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
typedef long long LL;
const int MOD = 1e9+7;const int maxn =2e5+100;
typedef long double db;
LL a[maxn];
LL n,K;
LL sum=0;
int fa[maxn];
int find(int x){
return fa[x]==x?x:fa[x] = find(fa[x]);
}
inline bool overflow_LL(LL a,LL b){
return a>LLONG_MAX/b;
}
void input(){
scanf("%I64d%I64d",&n,&K);
for (int i=1;i<=n;i++){
scanf("%I64d",a+i);
fa[i]=i;
sum+=a[i];
}
sum*=K;
}
void solve(){
for (int i=1;i<n;i++){
if(a[i]==1&&a[i+1]==1){
fa[find(i)] = find(i+1);
}
}
LL ans =0;
for (int i=1;i<=n;i++){
LL tempSum =K*a[i];
LL tempPro =a[i];
int j=i;
do{
if(tempSum==tempPro)ans++;
j++;
if(j>n)break;
if(find(j)==j){
tempSum+=K*a[j];
if(overflow_LL(tempPro,a[j]))break;
tempPro*=a[j];
}else{
int k = find(j);
LL l = tempSum+K;
LL r = tempSum+1LL*K*(k-j+1);
if(l<=tempPro&&tempPro<=r&&(tempPro-tempSum)%K==0){
int delta = (tempPro-tempSum)/K-1;
j +=delta;
tempSum=tempPro;
}else{
tempSum+=K*(k-j+1);
j = find(j);
}
}
}while (tempPro<=sum&&j<=n);
}
cout<<ans<<endl;
}
int main(){
input();
solve();
return 0;
}