Martial Arts Tournament(2100)前缀和,后缀和 Educational Codeforces Round 121 (Rated for Div. 2)

D. Martial Arts Tournament
time limit per test2 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
Monocarp is planning to host a martial arts tournament. There will be three divisions based on weight: lightweight, middleweight and heavyweight. The winner of each division will be determined by a single elimination system.

In particular, that implies that the number of participants in each division should be a power of two. Additionally, each division should have a non-zero amount of participants.

𝑛 participants have registered for the tournament so far, the 𝑖-th of them weighs 𝑎𝑖. To split participants into divisions, Monocarp is going to establish two integer weight boundaries 𝑥 and 𝑦 (𝑥<𝑦).

All participants who weigh strictly less than 𝑥 will be considered lightweight. All participants who weigh greater or equal to 𝑦 will be considered heavyweight. The remaining participants will be considered middleweight.

It’s possible that the distribution doesn’t make the number of participants in each division a power of two. It can also lead to empty divisions. To fix the issues, Monocarp can invite an arbitrary number of participants to each division.

Note that Monocarp can’t kick out any of the 𝑛 participants who have already registered for the tournament.

However, he wants to invite as little extra participants as possible. Help Monocarp to choose 𝑥 and 𝑦 in such a way that the total amount of extra participants required is as small as possible. Output that amount.

Input
The first line contains a single integer 𝑡 (1≤𝑡≤104) — the number of testcases.

The first line of each testcase contains a single integer 𝑛 (1≤𝑛≤2⋅105) — the number of the registered participants.

The second line of each testcase contains 𝑛 integers 𝑎1,𝑎2,…,𝑎𝑛 (1≤𝑎𝑖≤𝑛) — the weights of the registered participants.

The sum of 𝑛 over all testcases doesn’t exceed 2⋅105.

Output
For each testcase, print a single integer — the smallest number of extra participants Monocarp is required to invite after he chooses the weight boundaries 𝑥 and 𝑦.

Example
inputCopy
4
4
3 1 2 1
1
1
6
2 2 2 1 1 1
8
6 3 6 3 6 3 6 6
outputCopy
0
2
3
2
Note
In the first testcase of the example, Monocarp can choose 𝑥=2 and 𝑦=3. Lightweight, middleweight and heavyweight divisions will have 2, 1 and 1 participants, respectively. They all are powers of two, so no extra participants are required.

In the second testcase of the example, regardless of the choice of 𝑥 and 𝑦, one division will have 1 participant, the rest will have 0. Thus, Monocarp will have to invite 1 participant into both of the remaining divisions.

In the third testcase of the example, Monocarp can choose 𝑥=1 and 𝑦=2. Lightweight, middleweight and heavyweight divisions will have 0, 3 and 3 participants, respectively. So an extra participant is needed in each division.

In the fourth testcase of the example, Monocarp can choose 𝑥=8 and 𝑦=9. Lightweight, middleweight and heavyweight divisions will have 8, 0 and 0 participants, respectively. Middleweight and heavyweight division need an extra participant each.

题意 :

  • 给n个元素(每个元素的值 [ 1 , n ] [1,n] [1,n]),要求分为三组,第一组元素的值小于x,第三组大于y,第二组介于两者之间,且要求每组人数是2的幂次,求最少加入多少人,能满足需求

思路 :

  • 由于与每个值对应的个数有关,且值的范围已知,因此输入按值进行个数的桶排序,记为 n u m [ x ] num[x] num[x]
  • 在第一二三组人数皆满足2的幂次的情况下,枚举第一组和第三组的人数,只要使得第二组人数加上第一三组的原先的人数大于等于n即可,当前枚举的结果就是第一二三组人数之和减去n
  • 那么,如何通过当前枚举的人数得出原先该组内的人数呢?答案是通过前缀和,下标是预设人数,对应的值是原先有的人数;后缀和同理,只是从后往前推。即, p r e [ x ] pre[x] pre[x]表示第一组人数为x的情况下,原先第一组的人数; n x t [ x ] nxt[x] nxt[x]表示第三组人数为x的情况下,原先第三组的人数
#include <iostream>
#include <cstring>
#define endl '\n'
using namespace std;

const int N = 2e5 + 10;

int n, num[N];
int pre[N], nxt[N];

void solve()
{
    cin >> n;
    memset(num, 0, sizeof num);
    //memset(pre, 0, sizeof pre);
    //memset(nxt, 0, sizeof nxt);
    for (int i = 1, x; i <= n && cin >> x; i ++ ) num[x] ++ ;
    
    int sum = 0;
    for (int i = 1; i <= n; i ++ )
    {
        for (int j = sum; j < sum + num[i]; j ++ ) pre[j] = sum;
        sum += num[i];
    }
    
    sum = 0;
    for (int i = n; i; i -- )
    {
        for (int j = sum; j < sum + num[i]; j ++ ) nxt[j] = sum;
        sum += num[i];
    }
    
    int ans = 1 << 20;
    for (int i = 1; i <= n; i <<= 1)
        for (int j = 1; j <= n; j <<= 1)
        {
            int mid = 1;
            while (mid + pre[i] + nxt[j] < n) mid <<= 1;
            ans = min(ans, mid + i + j - n);
        }
    cout << ans << endl;
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    
    int _; cin >> _;
    while (_ -- )
        solve();
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值