D. Kousuke's Assignment 题解

前言

特别感谢 @jiejiejiang
这场div3给我打破防了,谢谢好兄弟对这么菜的我不离不弃 > <
本题也是他教我的,我在他思路的基础上做一点自己的补充
题目传送门:https://codeforces.com/contest/2033/problem/D
怎么感觉 TokaiZaopen 之前给我讲过类似的题(?)

题目大意

给一个序列,问和为0且不重叠的子区间的最大个数

思路

\([L,R]\)区间和为0,即前缀和相同,\(pre[r]-pre[l-1]=0\)
从左往右遍历,用set储存之前的累加结果,set初始有个0,
对于每一位,用sum累加。
如果在set里有相同的结果,ans++,清空set,并插入0,sum置零。

  • sum置零?
    1. 方便找直接为0的点
    2. \(pre[r]=pre[l-1]\) 那么左右两侧同时减去 \(pre[k]\) 也成立,也就是指要从同一个点开始求前缀和就可以,不一定得是从0
  • set清空? 要保证区间不重叠。

简单证明

对于两个和为0的区间A,B,共有三种关系:

  • 包含(AB只能择一)
  • 相交(AB只能择一)
  • 相离(AB需要全选)

如果用上述的贪心策略:

  • 对于A包含B,统计B,没有A,
  • 相交(A前B后),统计A,没有B
  • A,B相离,A和B都会统计上

对于区间个数这个属性来说,都是不影响的,所以贪心策略可行。

代码

参考了 @jiejiejiang 的

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
set<ll> st;
void solve();
void solve(){
ll n,x,sum=0,ans=0;
cin>>n;
st.clear();
st.emplace(0);
for(ll i=1;i<=n;i++){
cin>>x;
sum+=x;
if(st.find(sum)==st.end()){
st.emplace(sum);
} else {
ans++;
st.clear();
st.emplace(0);
sum=0;
}
}
cout<<ans<<'\n';
}
int main(){
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
ll t=1;
cin>>t;
while(t--){
solve();
}
return 0;
}

收获

  • \([l,r]\) 区间和为0 $\iff $ \(pre[r]==pre[l-1]\)
    赛时没意识到从同一个点求和即可
  • 自己证明了贪心的可行性
posted @   Gusare  阅读(86)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示