题解 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) 个物品拿上。

如果此时的价格之和为奇数,那么直接输出;

如果为偶数,那么

  1. 将已选的物品中最小的偶数价格删去,加入没选的物品中最大的奇数价格,

  2. 将已选的物品中最小的奇数价格删去,加入没选的物品中最大的偶数价格,

取以上两种做法中价格较大的作为答案输出。

改进:

考虑到数据范围过大,采用预处理的方式,将数据读入,把数据范围内可能的所有答案预处理出来,再读入每个 (m) 对当前的 (m) 进行在线输出。

  1. 预处理出在取到种数量的物品时的未选的最大奇数和偶数价格,以及已选的最小奇数和偶数价格,方便后续调用。

  2. 从少到多将所有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
", Ans[k]);
	return 0;
}

【补充】

快写很慢!!很慢!

Over

原文地址:https://www.cnblogs.com/bosswnx/p/10570804.html