Codeforces Round #400 Molly's Chemicals 前缀和

本文介绍了Codeforces Round #400中的一道题目,Molly想要通过混合化学品来制作爱情药水,其中化学品的吸引力值之和需要为k的非负整数次幂。文章详细解析了解题思路,包括如何使用前缀和技巧,并提供了两种不同情况(k为1和-1)下的解决方案。同时,还展示了错误的枚举方法并解释了其问题所在,最后给出了高效的解题代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

C. Molly's Chemicals
time limit per test
2.5 seconds
memory limit per test
512 megabytes
input
standard input
output
standard output

Molly Hooper has n different kinds of chemicals arranged in a line. Each of the chemicals has an affection value, The i-th of them has affection value ai.

Molly wants Sherlock to fall in love with her. She intends to do this by mixing a contiguous segment of chemicals together to make a love potion with total affection value as a non-negative integer power ofk. Total affection value of a continuous segment of chemicals is the sum of affection values of each chemical in that segment.

Help her to do so in finding the total number of such segments.

Input

The first line of input contains two integers, n and k, the number of chemicals and the number, such that the total affection value is a non-negative power of this number k. (1 ≤ n ≤ 1051 ≤ |k| ≤ 10).

Next line contains n integers a1, a2, ..., an ( - 109 ≤ ai ≤ 109) — affection values of chemicals.

Output

Output a single integer — the number of valid segments.

Examples
input
4 2
2 2 2 2
output
8
input
4 -3
3 -6 -3 12
output
3
Note

Do keep in mind that k0 = 1.

In the first sample, Molly can get following different affection values:

  • 2: segments [1, 1][2, 2][3, 3][4, 4];

  • 4: segments [1, 2][2, 3][3, 4];

  • 6: segments [1, 3][2, 4];

  • 8: segments [1, 4].

Out of these, 24 and 8 are powers of k = 2. Therefore, the answer is 8.

In the second sample, Molly can choose segments [1, 2][3, 3][3, 4].


这种求多少个区间满足条件的题,一直是我的一大软肋,0rzzz。

设sum[i]=sigma(a[0...i]) (其实i表示的就是前前缀和的右端点) 这个题意思就是,求sum[j]-sum[i]=k^p(p=0,1,2,3...)(j>=i)。

我自己能想到的解法也就只有暴力枚举了i,j了,O(n^2)还是死了这条心吧。其实还是枚举,不过我们得换一个姿势。

注意到k^p不超过1e14,那么最差情况下(1,-1分别讨论),我们需要枚举幂p [log2 (1e14)]+1<50故,我们可以考虑枚举p和右前缀和的右端点j也就是说 sum[j]-k^p=sum[i],sum[i]有几个答案ans就加上几,考虑用map存储个数。

code:

#include<cmath>
#include<map>
#include<cstdio>
using namespace std;
typedef long long LL;
const int MAXN=1e5+5;
const LL INF=1e15+5;
LL sum[MAXN];
map<LL,int>cnt;
int main(){
    LL n,k;scanf("%I64d%I64d",&n,&k);
    sum[0]=0;
    for(int i=1;i<=n;++i){
        LL a;scanf("%I64d",&a);
        sum[i]=sum[i-1]+a;
    }
    LL res=0;
    if(k==1){
        for(int i=0;i<=n;++i){
            cnt[sum[i]]++;//1的不论多少次方都是1
            res+=cnt[sum[i]-1];
        }
    }
    else if(k==-1){
        for(int i=0;i<=n;++i){
            cnt[sum[i]]++;//-1的奇数次方是-1,偶数次方是1
            res+=cnt[sum[i]-1]+cnt[sum[i]+1];
        }
    }
    else {
        for(int i=0;i<=n;++i){//枚举前缀和右端点
            cnt[sum[i]]++;
            LL val=1;//val 代表k的p次方的值
            for(;;){//枚举k^p
                res+=cnt[sum[i]-val];
                val*=k;
                if(val>INF||val<-INF)break;
            }
        }
    }
    printf("%I64d\n",res);
}

其实在写这个题期间我还犯了一个巨大的错误:

    sum[0]=0; cnt[0]++;
    for(int i=1;i<=n;++i){
        LL a;scanf("%I64d",&a);
        sum[i]=sum[i-1]+a;
        cnt[sum[i]]++;

    }

然后像这样枚举。

    int res=0;
    for(int i=0;i<=n;++i)
    for(int p=0;;++p){
        LL val=pow(k,p);
        if(val>INF||val<-INF)break;
        res+=cnt[sum[i]-val];//tag
        if(cnt[sum[i]-val]>=1){
            //printf("sum[i]=%I64d,val=%I64d\n",sum[i],val);
        }

错误的原因在于没理解到时枚举右端点,在tag处得到的值可能跑到右端点右边去了,这显然会重复。下次注意吧。

题外话:每次看前面的哪些神牛的代码都太感叹了。能够在这么短的时间内写出这么对的优雅的代码。贴一个ainta的

#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;
map<long long, int>Map;
int n, K;
long long s, res, t;
int main(){
    int a, i;
    scanf("%d%d",&n,&K);
    Map[0] = 1;
    for(i=1;i<=n;i++){
        scanf("%d",&a);
        s += a;
        if(K==1){
            res += Map[s-1];
        }
        else if(K==-1){
            res += Map[s-1] + Map[s+1];
        }
        else{
            t = 1;
            while(1){
                res += Map[s-t];
                t*=K;
                if(t > 1e15 || -t > 1e15)break;
            }
        }
        Map[s]++;
    }
    printf("%lld\n",res);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值