[CodeForces - 776C] - Molly's Chemicals

 题意:

  • 给定一个长度为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;
}
发布了242 篇原创文章 · 获赞 68 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_44049850/article/details/104504384
今日推荐