CF1260E Tournament 题解

发布时间 2023-05-05 18:46:33作者: Jerry_Jiang

妙妙题,但是感觉评不到紫。

题目链接

题意

luogu 题意。

\(n\) 个人,贿赂第 \(i\) 个人的代价为 \(a_i\)。这些人中,贿赂代价为 \(-1\) 的是你的朋友。现在,你可以两两配对,使得编号小的被淘汰,但是,如果你贿赂了编号大的,那么编号大的被淘汰,而编号小的留下。问:使得你朋友夺得冠军的最小代价。

题解

如果你的朋友不是第 \(n\) 个人,则必然要贿赂第 \(n\) 个人。

此时若你的朋友编号 \(\ge \frac{n}{2}\),则完成。否则需要取 \(\frac{n}{2}\sim n-1\) 的最小贿赂代价。

容易发现规律,即每 \(2^k\) 分一段,每一段取 \(a_i\) 最小值,直到 \(a_i=-1\) 停止。

时间复杂度 \(O(n)\)

总结

巨大妙妙。但是手算一下应该不难看出。

代码

#include<bits/stdc++.h>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define sz(x) (int)x.size()
#define ll long long
#define pii pair<int,int>
using namespace std;
inline void chmin(int &x,int y){x=min(x,y);}
inline void chmax(int &x,int y){x=max(x,y);}
const int N=(1<<18)+5;
int n;
ll a[N];
priority_queue<ll,vector<ll>,greater<ll>> pq;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%lld",a+i);
	ll ans=0;
	for(int i=n;i>=1;i--){
		if(a[i]==-1) break;
		pq.push(a[i]);
		if(__builtin_popcount(i)==1){
			ans+=pq.top();
			pq.pop();
		}
	}
	printf("%lld\n",ans);
	return 0;
}
/*
Basic:
1. int or long long?
2. freopen?
3. memory limits?
Advanced:
1. use more functions
2. write carefully and check
3. think twice before writing
4. debug quickly
5. never copy others' codes
*/