【CodeChef】3out1in(优先队列)

题目大意:

给出数组a,问对于所有满足\(1\le k\le n\)的奇数\(k\)\(f([a_1,a_2,...,a_k])\)的值。\(f([a_1,a_2,...,a_n])\)的值为对数组\([a_1,a_2,...,a_n]\)进行\(\frac{n+1}{2}\)次操作(选择数组中的三个元素,将其中一个取相反数,然后让它们合并成一个元素)后,数组最后剩下元素的最大值。


考虑数组中每个元素对答案的贡献(答案为所有元素的贡献之和)。

操作之前,数组中第\(i\)个元素对答案的贡献为\(a_i\)。每经过一次操作后,其中一个元素对答案的贡献就会变成相反数(这个元素的贡献变成相反数后不会再通过操作改变贡献)。因此经过\(\frac{k+1}{2}\)次操作之后,会有\(\frac{k+1}{2}\)个元素的贡献变成相反数。

因此,如果我们要让贡献之和最大,只需要将\([a_1,a_2,...,a_k]\)中最小的\(\frac{k+1}{2}\)个元素取相反数即可。

具体实现中,我们可以开两个优先队列。\(k\)\(1\)枚举到\(n\)的过程中,队列1储存较小的\(\frac{k+1}{2}\)个元素,队列2储存剩下较大的元素。对于每个\(k\),答案即为队列2元素之和减去队列1的元素之和。

#include<bits/stdc++.h>
#define pt printf(">>>")
#define mid (((l)+(r))/2)
using namespace std;
typedef long long ll;
const ll N=1e6+10,inf=1e18+10,mod=1e9+7;
ll n,q,a[N],dp[N];
int main(){
	int T;
	cin >> T;
	while(T--){
		cin >> n >> q;
		for(ll i=1;i<=n;i++)cin >> a[i];
		priority_queue<ll> que1;
		priority_queue<ll,vector<ll>,greater<ll> > que2;
		que1.push(-inf);
		que2.push(a[1]);
		que2.push(inf);
		ll sum1=0,sum2=a[1];
		for(ll i=1;i<=n;i+=2){
			dp[i]=sum2-sum1;
			if(i<n){
				if(a[i+1]<=que1.top())que1.push(a[i+1]),sum1+=a[i+1];
				else que2.push(a[i+1]),sum2+=a[i+1];
				if(a[i+2]<=que1.top())que1.push(a[i+2]),sum1+=a[i+2];
				else que2.push(a[i+2]),sum2+=a[i+2];
				while(que1.size()<i/2+1+1){
					ll v=que2.top();que2.pop();
					que1.push(v);
					sum1+=v,sum2-=v;
				}
				while(que1.size()>i/2+1+1){
					ll v=que1.top();que1.pop();
					que2.push(v);
					sum1-=v,sum2+=v;
				}
			}
		}
		while(q--){
			ll k;
			cin >> k;
			cout << dp[k] << ' ';
		}
		cout << endl;
	}
	return 0;
}
posted @ 2024-05-16 15:19  Alric  阅读(8)  评论(0编辑  收藏  举报