題目:
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.
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 ≤ 105, 1 ≤ |k| ≤ 10).
Next line contains n integers a1, a2, ..., an ( - 109 ≤ ai ≤ 109) — affection values of chemicals.
Output a single integer — the number of valid segments.
4 2 2 2 2 2
8
4 -3 3 -6 -3 12
3
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, 2, 4 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);
}