cf2121f
CF2121F Yamakasi
题意
给定序列 \(a\),以及两个整数 \(s,x\),计算有多少个子段和为 \(s\) 且最大值为 \(x\)。多测。\(n\leq 2\times 10^5,-10^9\leq a_i,x\leq 10^9,-2\times 10^{14}\leq s\leq 2\times 10^{14}\)。
题解
将子段和转化为前缀和相减,即 \(f_r-f_{l-1}\)。枚举右端点,考虑用 map
来直接统计答案,即 \(f_r-s\)。还需保证最大值,若 \(a_r>x\),则说明 \(a_r\) 必然不能存在于答案子段中,将 map
清空,因为前面的都无法对后面的产生贡献。这里记一个左端点 \(l\),表示后面的左端点至少要取到 \(l\) 后面。每当有 \(a_i=x\),就把对应区间加一下,最后每次统计即可。复杂度 \(O(n\log n)\)。aclink。
坑
- 代码简单。
代码
#include<bits/stdc++.h>
#define i64 long long
#define L(a,b,c,d) for(int a=b;a<=c;a+=d)
#define R(a,b,c,d) for(int a=b;a>=c;a-=d)
using namespace std;
const int N=3e5+5;
void solve();
int n,x,a[N];
i64 s,f[N];
map<i64,int> mp;
signed main(){
int Test=1;
scanf("%d",&Test);
while(Test--) solve();
return 0;
}
void solve(){
scanf("%d%lld%d",&n,&s,&x);
f[0]=0;
L(i,1,n,1){
scanf("%d",a+i);
f[i]=f[i-1]+a[i];
}
mp.clear();
i64 ans=0;
int pos=0;
L(i,1,n,1){
if(a[i]>x){
mp.clear();
pos=i;
}
else{
if(a[i]==x){
while(pos<i) mp[f[pos++]]++;
}
ans+=mp[f[i]-s];
}
}
printf("%lld\n",ans);
}
本文来自博客园,作者:jess1ca1o0g3,转载请注明原文链接:https://www.cnblogs.com/jess1ca1o0g3/p/19062357