題解 【[長春集訓7.22]序列】
阿新 • • 發佈:2021-07-29
[長春集訓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; }