Loading

cf2121f

CF2121F Yamakasi

link

题意

给定序列 \(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);
}
posted @ 2025-08-28 11:47  jess1ca1o0g3  阅读(3)  评论(0)    收藏  举报