题解 【[长春集训7.22]序列】
作者:互联网
[长春集训7.22] 序列
(密码 wyca)
题目大意:
给一个序列,找一个区间使得该区间 \([\,l,\,r]\) 的和为正奇数,且和最小。输出最小值和该区间的左右端点 \(l,r\) 。
solution:
考场上我先打了 三十分\((n\leq1000)\)的暴力,然后想题目给的\(20\)分特殊性质: \(a_i\ge0\)。对于一个奇数位我就选它本身,而对于一个偶数位,找到它右端第一个奇数,求和。右端第一个奇数可以通过倒序处理出,求和通过预处理前缀和,期望得分\(50\)分。然后就写挂了,还是老实打暴力再剪枝优化吧[捂脸]。
正解是开两个 \(\text{set}\) 分别存为奇偶的前缀和。对于一个位置的前缀和 \(pre\) :
- 如果 \(pre\) 为奇数,为了得出最小正奇数,应找到小于 \(pre\) 的最大偶 \(pre\)
- 如果 \(pre\) 为偶数操反之。
这个可以在 \(\text{set}\) 中 \(\text{lowerbound}\) 找出。
细节处理:
我们开两个 \(\text{pair}\) 类型的 \(\text{set}\) 第一维存 \(pre\) ,第二维存位置。
倒序循环便于求出字典序最小的 \(l,r\) 。
代码
#include<cstdio>
#include<set>
using namespace std;
const int N=3e5+5;
typedef long long LL;
set<pair<LL,int> > s[2];
LL a[N],sum[N];
int main(){
int n; scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
sum[i]=sum[i-1]+a[i];
}
set<pair<LL,int> >::iterator it;
pair<LL,int> res;
int l=-1,r=-1;LL ans=9223372036854775807;
for(int i=n;i>=0;i--){
bool p=sum[i]&1;
res=make_pair(sum[i],i);
s[p].insert(res);
it=s[p^1].lower_bound(res);
if(it!=s[p^1].end()&&it->first-sum[i]<=ans){
ans=it->first-sum[i],l=i+1,r=it->second;
}
}
if(l==-1&&r==-1) printf("-1");
else printf("%lld %d %d",ans,l,r);
return 0;
}
End
标签:pre,set,奇数,int,题解,sum,7.22,text,集训 来源: https://www.cnblogs.com/TSZ-think/p/15076808.html