其他分享
首页 > 其他分享> > 题解 【[长春集训7.22]序列】

题解 【[长春集训7.22]序列】

作者:互联网

[长春集训7.22] 序列

(密码 wyca)

题目大意:

给一个序列,找一个区间使得该区间 \([\,l,\,r]\) 的和为正奇数,且和最小。输出最小值和该区间的左右端点 \(l,r\) 。

solution:

考场上我先打了 三十分\((n\leq1000)\)的暴力,然后想题目给的\(20\)分特殊性质: \(a_i\ge0\)。对于一个奇数位我就选它本身,而对于一个偶数位,找到它右端第一个奇数,求和。右端第一个奇数可以通过倒序处理出,求和通过预处理前缀和,期望得分\(50\)分。然后就写挂了,还是老实打暴力再剪枝优化吧[捂脸]。

正解是开两个 \(\text{set}\) 分别存为奇偶的前缀和。对于一个位置的前缀和 \(pre\) :

  1. 如果 \(pre\) 为奇数,为了得出最小正奇数,应找到小于 \(pre\) 的最大偶 \(pre\)
  2. 如果 \(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;
}

set用法

End

标签:pre,set,奇数,int,题解,sum,7.22,text,集训
来源: https://www.cnblogs.com/TSZ-think/p/15076808.html