题目描述
有n件商品,选出其中的k个,要求它们的总价为奇数,求最大可能的总价。
做法
首先肯定要取一个奇数,所以一定取最大的奇数。
一个奇数都没有可以直接-1了。
接下来奇数只能两个两个取,而偶数可以一个一个取。
而且肯定从大到小,所以先排个序。然后num[x]表示取x对奇数前缀和,sum[x]表示取x个偶数前缀和。
假设要取k个,而且我们取了x对奇数,贡献是num[x]+sum[k-2x]
所有询问当然根据k从小到大做啦,然后考虑决策单调性,即如何找到最优x。
对于两个合法决策x和y满足
x<y
(注意我们做的过程只能考虑合法的决策,合法决策x需满足k-2x>=0且k-2x<=top,top是偶数个数,前者的满足可以每次k改变时把新的合法决策加入,后者则在计算答案时才去检验是否合法),随着k的变大,y决策会越来越比x优(因为我们知道k改变时sum[k-2y]的增量会比sum[k-2x]大),因此可以通过二分计算两个决策在k达到多少时y会比x优。
而当y比x优时,y便永远的比x优了,x也就变成了冗余状态。
考虑维护单调队列,维护交点的单调递增(因为k递增),每次取答案时不断检验队头是否合法(你会发现因为按小到大加入队列中,在队列前面的越容易非法),假如队列空了该询问肯定是-1。
详见代码,复杂度O(n log n)
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=1000000+10;
struct dong{
int k,id;
} ask[maxn];
int a[maxn],b[maxn],c[maxn],dl[maxn],xy[maxn],sta[70];
ll ans[maxn],num[maxn],sum[maxn];
int i,j,k,l,t,n,m,tot,top,mx,head,tail,cnt;
bool czy;
int read(){
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
bool cmp(int a,int b){
return a>b;
}
bool cmp2(dong a,dong b){
return a.k<b.k;
}
void write(ll x){
if (x<0){
putchar('-');
x=-x;
}
if (!x){
putchar('0');
putchar('\n');
return;
}
cnt=0;
while (x){
sta[++cnt]=x%10;
x/=10;
}
while (cnt){
putchar('0'+sta[cnt]);
cnt--;
}
putchar('\n');
}
int getxy(int x,int y){
//if (2*y>2*x+top) return 2*y;
int l=2*y,r=2*x+top+1,mid;
while (l<r){
mid=(l+r)/2;
if (num[x]+sum[mid-2*x]<=num[y]+sum[mid-2*y]) r=mid;else l=mid+1;
}
return l;
}
void insert(int x){
while (head<tail&&xy[tail]>=getxy(dl[tail],x)) tail--;
dl[++tail]=x;
if (head<tail) xy[tail]=getxy(dl[tail-1],x);
}
int main(){
freopen("3721.in","r",stdin);freopen("3721.out","w",stdout);
czy=1;
n=read();
fo(i,1,n){
a[i]=read();
if (a[i]%2==1) b[++tot]=a[i];else c[++top]=a[i];
}
if (tot==0) czy=0;
m=read();
if (!czy){
fo(i,1,m) write(-1);
return 0;
}
sort(b+1,b+tot+1,cmp);
mx=b[1];
fo(i,2,tot) b[i-1]=b[i];
tot--;
sort(c+1,c+top+1,cmp);
fo(i,1,tot/2) num[i]=num[i-1]+(ll)b[i*2-1]+(ll)b[i*2];
tot/=2;
fo(i,1,top) sum[i]=sum[i-1]+(ll)c[i];
fo(i,1,m){
ask[i].k=read();
ask[i].k--;
ask[i].id=i;
}
sort(ask+1,ask+m+1,cmp2);
j=0;
head=1;
fo(i,1,m){
while (j<=tot&&j*2<=ask[i].k){
insert(j);
j++;
}
while (head<=tail&&ask[i].k-2*dl[head]>top) head++;
while (head<tail&&ask[i].k>=xy[head+1]) head++;
if (head>tail) ans[ask[i].id]=-1;
else ans[ask[i].id]=(ll)mx+num[dl[head]]+sum[ask[i].k-2*dl[head]];
}
/*if (!czy){
fo(i,1,m) ans[i]=-1;
}*/
fo(i,1,m) write(ans[i]);
}