题意:
- 给定一个长度为n的数列,找到有多少个区间和是k的次幂。
思路:
- 我们要找到区间(l, r],有sum[ r ] - sum[ l ] == power(k, ...)
- n^2的复杂度肯定不行,所以我们想该如何优化?
- 由数据范围我们可以知道,k的次幂在1e14内。那么我们可不可以转换为找到是不是存在sum[ l ] = sum[ r ] - pow[ k ]. 这样我们就变成了枚举pow[ ](最坏O(log(1e14)))和 sum[ ](O(n))再加上map的O(nlogn),复杂度可行!
- 我们外层枚举k的次幂,内层枚举sum[r],然后在内层统计答案ans += mp[sum[r] - p]
至于为什么在内层统计ans,想了好久。我开始认为会有重复的。因为可能会有很多值等于sum[ i ] - p(对于不同的i和p来说),但是我没有想到的是区间问题。
首先,我们枚举的是sum[r],言外之意就是找到[1, r]区间中是不是有[l, r]刚好是k的次幂
当i递增,我们寻找的区间就变成了[l, 更大的r]
也就是说我们枚举的区间是[1, 1]、[1, 2]、[1, 3]、[1, 4] ... [1, n]找的是固定右端点的区间,而且保证了区间的有效性,也不会重复……
而如果我们不在循环里面统计,而是在循环外面统计,我们就不能保证l和r构成的区间是有效区间,会造成混淆
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
inline ll read()
{
ll x = 0, f = 1; char c = getchar();
while (c < '0' || c > '9') { if (c == '-') f = -f; c = getchar(); }
while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
const int maxN = 100005;
int n, k;
ll sum[maxN];
map<ll, ll> mp; //前缀和为first的区间有多少个
int main()
{
n = read(); k = read();
for(int i = 1; i <= n; ++ i )
sum[i] = read(), sum[i] += sum[i - 1];
ll p = 1;
ll ans = 0;
while(abs(p) <= 1e14) //枚举次幂
{
mp.clear();
mp[0] = 1;
for(int i = 1; i <= n; ++ i ) //枚举sum[r]
{ //到r = i当前为止,前缀区间和为tot = sum[r] - p的区间个数
//保证了sum[l]和sum[r]是有效区间
// 也就是说即使后面还会有等于tot的区间和,但是那个区间是大于当前sum[r]的范围的
ans += mp[sum[i] - p];
++ mp[sum[i]]; //作为sum[l]的个数+1
}
p *= k;
if(p == 1) //同时考虑了1和-1的情况
break;
}
printf("%lld\n", ans);
return 0;
}