沪粤二月月赛Div.1T1题解
为了防止有人没报名,我们放个题目:
题目描述
在有 \(N\) 人参加的游戏比赛中,作为交流,决定组成两人一组,以两人的合计分数来进行竞赛。刚好人数能被 \(2\) 整除,所以没有剩余的人。排名在前 \(M\) 名以内的组会获得奖品。如果有多个组的分数和第 \(M\) 名相同,那么所有这些分数相同的组也都能够获得奖品。
小明因为对参加者进行了仔细的调查,所以知道每个人能取得多少分数。为了无论剩下的人如何组队都能确保获得奖品,小明最少要和多少分的人组队呢?请求出这个分数。
如果无论和谁组队,小明都无法获得奖品,那么请输出 \(-1\)。
输入
第一行两个整数 \(N,M\)。
后续 \(N\) 行,第 \(i\) 行一个整数 \(a_i\) 表示第 \(i\) 个人的分数。其中,\(a_1\) 是小明的分数。
输出
输出一行,一个整数,表示确保获得奖品的情况下,小明最少要和多少分的人组队。如果无解,输出 \(-1\)。
样例输入输出
样例输入 #1
8 2
6
1
1
2
4
4
4
4
样例输出 #1
2
样例说明 #1
组队 \((6,2)\)、\((4,4)\)、\((4,4)\) 和 \((1,1)\) 时,小明依然可以获得奖品。如果选择和得分为 \(1\) 的组队,那么无法获得奖品。
样例输入 #2
6 1
1
1
4
5
1
4
样例输出 #2
-1
首先我们发现答案显然有单调性,于是我们可以二分,然后重点是如何 check?首先我们如果决定将 \(a_{mid}\) 和小明配对,那么我们就得找到其他的人最有可能排挤掉小明那一对的配对方式,看一下能否排挤掉,如果排挤不掉,这个配对就是一个解,如果能排挤掉,这个配对就不是一个解,那如何配对?考虑贪心,如果对于一个数无论怎样配对都无法配对出一个得分大于小明的配对的得分,那么这个数就是没用的,我们找一个当前最小的可用的数配对就行了(注意!!这里不能直接随便找一个可用的数配对,因为这个数都没用了,我们应该让它挑选一个最小的可用的数来配对,避免占用了后面有用的数配对),然后如果这个数有用,那么我们就找到当前最小的可用的并且可以使这个配对的得分大于小明的配对的得分的数来配对(注意!!这里不能直接随便找一个可用的并且可以使这个配对的得分大于小明的配对的得分的数配对,因为这个数虽然有用,我们也应该让它挑选一个最小的可用的并且可以使这个配对的得分大于小明的配对的得分的数来配对,避免占用了后面有用的但是更小的数找搭配),于是这个题就做完了。
考场代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
int b[N];
signed main()
{
int n,m;
scanf("%d %d",&n,&m);
int one;
scanf("%d",&one);
map<int,int>mppp;
for(int i = 2;i<=n;i++)
{
scanf("%d",&b[i-1]);
mppp[b[i-1]]++;
}
sort(b+1,b+n);
int tot = unique(b+1,b+n)-b-1;
int l = 1,r = tot,ans = 0;
while(l<=r)
{
int mid = l+r>>1;
map<int,int>mp = mppp;
auto it = mp.find(b[mid]);
it->second--;
if(!it->second)
{
mp.erase(it->first);
}
map<int,int>mpp;
mpp[one+b[mid]] = 1;
for(auto it = mp.begin();it!=mp.end();it = mp.begin())
{
while(it->second)
{
auto s = mp.upper_bound(one+b[mid]-it->first);
if(s == it)
{
if(it->second>1)
{
it->second-=2;
mpp[it->first*2]++;
}
else
{
it->second--;
auto itt = it;
itt++;
itt->second--;
mpp[it->first+itt->first]++;
if(!itt->second)
{
mp.erase(itt->first);
}
}
}
else
{
if(s == mp.end())
{
if(it->second>1)
{
it->second-=2;
mpp[it->first*2]++;
}
else
{
it->second--;
auto itt = it;
itt++;
itt->second--;
mpp[it->first+itt->first]++;
if(!itt->second)
{
mp.erase(itt->first);
}
}
}
else
{
it->second--;
s->second--;
mpp[it->first+s->first]++;
if(!s->second)
{
mp.erase(s->first);
}
}
}
}
mp.erase(it->first);
}
int num = 0,flag = 0;
for(auto it = mpp.rbegin();it!=mpp.rend();it++)
{
num+=it->second;
if(num>=m)
{
if(one+b[mid]>=it->first)
{
flag = 1;
}
break;
}
}
if(flag)
{
ans = mid;
r = mid-1;
}
else
{
l = mid+1;
}
}
printf("%d",!ans?-1:b[ans]);
return 0;
}
由于需要 map,所以时间复杂度上升为 \(O(n \log^2 n)\),但是依旧可以通过。
注意:最好不要使用 unordered_map,因为这玩意最坏时间复杂度是 \(O(n)\),效率远不如 map 的 \(O(\log n)\)。
如果有大佬可以将我的解法优化至 \(O(n \log n)\),欢迎评论区留言指出,我会关注您并且修改!!感谢支持!!

浙公网安备 33010602011771号