题解 DTOJ #2305.Bazarek
欢迎访问 My Luogu Space。
【题目描述】
小Bajtek和他的奶奶在度假。每天早晨,奶奶会去一个集市(bazarek)上买东西。男孩很快发现一个规律:他的奶奶每天的购物金额都是奇数。毕竟人老了就会有些死板嘛,每个祖母或多或少都这样。 男孩会提供含有n个商品的清单,奶奶从里面挑选若干个购买。每一天,奶奶会告诉男孩她要买k个商品,然后让男孩带够钱跟她去。但是他并不知道奶奶会买哪k个商品(但它们总价一定是奇数),所以他想知道这一天至少要带多少钱。
【输入输出格式】
输入格式:
第一行一个整数n(1<=n<=1000000),表示商品数量。
接下来一行有n个整数,表示每件商品的价格,范围在[1,10^9]。
接下来一行有一个整数m(1<=m<=1000000),表示询问数。
接下来m行,每行一个整数ki。
输出格式:
对于每个询问,输出一行表示至少要带多少钱。若无法满足要求,输出-1。
【输入输出样例】
输入样例:
4
4 2 1 3
3
2
3
4
输出样例:
7
9
-1
【标签】
模拟,预处理,读优。
【分析】
考虑 预处理+在线 的做法。
基本想法:
先将所有物品按从大到小排序,对于每个访问,先从大到小将 \(k\) 个物品拿上。
如果此时的价格之和为奇数,那么直接输出;
如果为偶数,那么
-
将已选的物品中最小的偶数价格删去,加入没选的物品中最大的奇数价格,
-
将已选的物品中最小的奇数价格删去,加入没选的物品中最大的偶数价格,
取以上两种做法中价格较大的作为答案输出。
改进:
考虑到数据范围过大,采用预处理的方式,将数据读入,把数据范围内可能的所有答案预处理出来,再读入每个 \(m\) 对当前的 \(m\) 进行在线输出。
-
预处理出在取到种数量的物品时的未选的最大奇数和偶数价格,以及已选的最小奇数和偶数价格,方便后续调用。
-
从少到多将所有k的答案预处理出来,记录到答案数组当中。读入 \(k\),直接输出 \(Ans[k]\)。
【代码 】
[C++]
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL MAXN = 1000000+5;
int n, m, S[MAXN], MaxOd[MAXN], MaxEv[MAXN]; //odd n.奇数 ,even n.偶数
LL Ans[MAXN], sum;
inline int rd(){ //读入优化
char s;
while(!isdigit(s=getchar()));
int x=(s^48);
while(isdigit(s=getchar())) x = (x<<1)+(x<<3)+(s^48);
return x;
}
int main(){
n=rd();
for(int i=1; i<=n; i++) S[i]=rd();
sort(S+1, S+n+1, greater<int>()); //从大到小排序
int odd = 0, eve = 0;
for(int i=n; i>=1; i--){ //处理所有情况下的未选最大奇数偶数价格,因为数组有序,直接更新,下同
MaxOd[i] = odd;
MaxEv[i] = eve;
if(S[i]&1) odd = S[i];
else eve = S[i];
}
odd = eve = 0;
for(int i=1; i<=n; i++){ //处理出所有答案
if(S[i]&1) odd = S[i]; //处理出已选最小奇数偶数价格
else eve = S[i];
sum += S[i];
if(sum&1) Ans[i] = sum;
else{
if(MaxOd[i] && eve) Ans[i] = max(Ans[i], sum-eve+MaxOd[i]); //如果能取才取,取二者最大值
if(MaxEv[i] && odd) Ans[i] = max(Ans[i], sum-odd+MaxEv[i]);
if(!Ans[i]) Ans[i] = -1;
}
}
int k;
for(m=rd(); m--;) k = rd(), printf("%lld\n", Ans[k]);
return 0;
}
【补充】
快写很慢!!很慢!