P10429 [蓝桥杯 2024 省 B] 拔河
P10429 [蓝桥杯 2024 省 B] 拔河
📌 问题描述
题目
给定序列 a,选择两个不重叠的连续子段 [l1,r1] 和 [l2,r2](满足 r1 < l2),
使它们的区间和之差最小。
约束条件
\(1 \leq n \leq 10^3\),\(1 \leq a_i \leq 10^9\)
💡 核心思路
设两个子段分别为 [l1,r1] 和 [l2,r2],并满足 r1 < l2。
对于区间 [l,r] 的元素和,可以用前缀和快速计算:
sum(l,r) = pre[r] - pre[l-1]
暴力枚举两个区间的复杂度为 O(n^4),无法通过。
因此考虑枚举右区间的左端点 l2。
固定 l2 后,所有合法的左区间都必须满足 r1 < l2,
而右区间则形如 [l2,r2]。
于是问题转化为:
对于每个右区间和 x = sum(l2,r2),
在所有满足 r1 < l2 的左区间和中,找到一个最接近 x 的值。
为此可以维护一个有序集合 st,
其中存储当前所有满足 r1 < l2 的左区间和。
当 l2 从小到大递增时,之前右端点更小的左区间已经加入过 st,
因此新增加的合法左区间恰好是所有右端点为 l2-1 的区间,
将它们的区间和加入 st 即可。
然后枚举右端点 r2,计算右区间 [l2,r2] 的区间和 x,
再用 lower_bound 在 st 中找到第一个大于等于 x 的值 it。
由于集合有序,与 x 最接近的值只可能是 it 或 it 的前一个元素,
比较这两者即可更新答案。
📋 模板步骤
预处理前缀和
vector<LL>a(n+1);
vector<LL>pre(n+1,0);
rep(i,1,n+1) cin>>a[i];
rep(i,1,n+1) pre[i]=pre[i-1]+a[i]; //pre[1]=pre[0]+a[1] pre[n]=pre[n-1]+a[n]
有序的st
set<LL>st;
枚举l2
rep(l2,2,n+1){
将新增的以l2-1为右端的字段和插入st
rep(j,1,l2){// [j,l2-1] [0,0] [l2-1,l2-1]
st.insert(pre[l2-1]-pre[j-1]);
}
枚举r2
rep(r2,l2,n+1){ //[l2,r2] [l2,l2] [n,n]
x=pre[r2]-pre[l2-1];
寻找st中最接近的子段和
auto it=st.lower_bound(x);
如果it之前有数,则考虑在内
if(it!=st.begin()) ans=min(llabs(*prev(it)-x),ans);
把it考虑在内
if(it!=st.end()) ans=min(llabs(*it-x),ans);
}
}
代码实现
完整代码
#include <bits/stdc++.h>
#define all(a) a.begin(),a.end()
#define rall(a) a.rbegin(),a.rend()
#define LL long long
#define MAX INT_MAX
#define LMAX LLONG_MAX
#define rep(i,a,b) for(int i=(int)a;i<int(b);++i)
using namespace std;
void solve(){
int n;
cin>>n;
vector<LL>a(n+1);
vector<LL>pre(n+1,0);
rep(i,1,n+1) cin>>a[i];
rep(i,1,n+1) pre[i]=pre[i-1]+a[i]; //pre[1]=pre[0]+a[1] pre[n]=pre[n-1]+a[n]
LL ans=LMAX,x=0;
set<LL>st;
rep(l2,2,n+1){
rep(j,1,l2){// [j,l2-1] [0,0] [l2-1,l2-1]
st.insert(pre[l2-1]-pre[j-1]);
}
rep(r2,l2,n+1){ //[l2,r2] [l2,l2] [n,n]
x=pre[r2]-pre[l2-1];
auto it=st.lower_bound(x);
if(it!=st.begin()) ans=min(llabs(*prev(it)-x),ans);
if(it!=st.end()) ans=min(llabs(*it-x),ans);
}
}
cout<<ans<<'\n';
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int tc=1;
//cin>>tc;
while(tc--){
solve();
}
return 0;
}
📊 复杂度分析
时间复杂度:\(O(n^2 \log n)\)
- 外层枚举
l2:\(O(n)\) - 中层枚举
j:\(O(n)\),插入set为 \(O(\log n)\) - 内层枚举
r2:\(O(n)\),查询set为 \(O(\log n)\)
空间复杂度:\(O(n)\)
- 数组存储及
set中最多 \(O(n^2)\) 个元素
重点
st 中始终维护所有满足 r1 < l2 的左区间和;对当前右区间和 x,只需在 st 中找最接近 x 的值。

浙公网安备 33010602011771号