Problem Description
输入n, k代表有n个元素,问你任意取一段区间和 满足k的m次方(m是任意正整数)让你求出:有多少段不同的区间和?
思路:
第一想法能想到的就是前缀和然后 sum[i] - sum[j] = pow[k] i: 1 to n; j : 0 to (i - 1); k : 0 to m;这样复杂度肯定会超时。于是就没想到了,然后百度借鉴借鉴。将公式转化成sum[i] - pow[k] = sum[j]这样i: 1 to n; k: 0 to m; j: 0 to (i - 1); 这时候 j可以在跑i的时候处理一下,复杂度就降到了n * m就够了注意inf的范围。
#include<bits/stdc++.h>
using namespace std;
#define nn 100055
#define inf 200000000000005//最大的两倍因为有负数
long long sum[nn];//前缀和
long long Pow[nn];//用来存k的m次方
int main()
{
int n, i, j, m;
long long k, num;
while(~scanf("%d %lld", &n, &k))
{
map<long long, int> q;
memset(sum, 0, sizeof(sum));
for(i = 1; i <= n; i++)//求前缀和
{
scanf("%lld", &num);
sum[i] = sum[i - 1] + num;
}
Pow[0] = 1;//求k的m次方
m = 0;
if(k != 1 && k != -1)//k得判断下特殊情况1 和 -1
for(i = 1; abs(Pow[i-1] * k) < inf; i++)
{
Pow[i] = Pow[i - 1] * k;
m = max(m, i);
}
else if(k == -1)
{
Pow[1] = -1; m = 1;
}
q[0] = 1;//相当于sum[0]
long long ans = 0;
for(i = 1; i <= n; i++)
{
for(j = 0; j <= m; j++)
{
long long d = sum[i] - Pow[j];
if(q.count(d))//如果存在
ans += q[d];//更新ans
}
if(q.count(sum[i]))//在跑i的过程 处理sum[j]这样复杂度就降下来了
q[sum[i]]++;
else q[sum[i]] = 1;
}
printf("%lld\n", ans);
}
return 0;
}