题目
n(n<=2^18)个人进行两两比赛,胜者晋级下一轮,败者淘汰
第i个人的实力值是i,对任意一轮,你都可以指定还没淘汰的人如何配对比赛,用他们的实力值比大小,
特别地,对于第i个人,你的朋友可以用ai(1<=ai<=1e9)的代价去贿赂他,使其直接被淘汰
你的朋友也出现在这个序列中,用-1标记,
这样log轮后就只剩一人,求你的朋友最后成为冠军所花费的最小代价
思路来源
卿学姐的B站讲解
题解
考虑以下图形,不难发现,
①无论如何指派,1都会在第一轮被淘汰, 23在第二轮,以此类推,
通过合理安排,第k轮可以存活的最小能力值的人是2^k,所以第k轮过后前(2^k)-1个人一定被淘汰
因此,要尽量让小的值和朋友在靠前的轮比赛,
这样朋友碰到的对手就是越来越强的,也说明了能不贿赂就不贿赂
②朋友如果是4,则最多只需要贿赂1轮,即贿赂8,
最后一轮只能贿赂8,倒数第二轮可以贿赂[5,8],其实是①得出的结论,以此类推
③比自己能力值小的人,显然不用贿赂
基于以上三点,倒序考虑轮次,
当遇到2的幂次的时候,再被迫贿赂代价最小的
代码
#include<bits/stdc++.h>
using namespace std;
const int N=(1<<18)+5;
typedef long long ll;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
#define sci(a) scanf("%d",&(a))
int n,a[N];
bool ok[N];
priority_queue<int,vector<int>,greater<int> >q;
ll ans;
int main(){
sci(n);
rep(i,1,n){
sci(a[i]);
}
rep(i,0,18){
ok[1<<i]=1;
}
per(i,n,1){
if(a[i]==-1)break;
q.push(a[i]);
if(ok[i]){
ans+=q.top();
q.pop();
}
}
printf("%lld\n",ans);
return 0;
}