1. 程式人生 > 其它 >題解 【[長春集訓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