题解 P2777 【[AHOI2016初中组]自行车比赛】
abc123_abc123 · · 题解
题目:luogu P2777
题目大意:有n个人1~i,已有得分ai,本轮发放每人1~n分,不重复,问有几人可能夺冠。
读题后,我们可以很快得到贪心策略:
若i要胜出,则其他人分数要小于他的分数,所以我们把其他人排序,最大的+1,次大的+2,以此类推。最后如果他的分数+n是最大的,则可能成为冠军。
为什么呢?如果j比i高分,j与k交换本轮得分使j所得分更少,则k将得到更大的得分,按我们之前的策略,k之前一定比j高分,则k与j交换本轮得分后分数一定大于i。
但是我们每次都做O(nlogn)的check的话,好像会T。。。于是我们可以先排序,再每次O(n)check排名为i的人。
于是我们神奇的发现:如果一个低分的人可能得冠,比他高分的人也可以。
So?通过这一点我们想到了排序后从大往小check排名为i的人,看有几个能成为冠军,这样还是O(n^2),还是会T。。。
突然灵光一闪,这里的有序性使我们可以二分答案!
方法出来了!先O(nlogn)排序,然后二分答案log(n)次,每次O(n)check,总的时间复杂度还是O(nlogn)。
贴上代码:
#include<cstdio>
#include<algorithm>
using namespace std;
int ans,a[300010],n,i,l,r,mid,p,j;
inline bool check(int x)
{
p=a[x]+n; //如果x要夺冠,先给他最高分
for(i=1,j=n-1;i<=n;i++)
{
if(i==x)continue; //是自己则跳过
if(a[i]+j>p)return 0; //最优情况下有更高则不能夺冠
j--; //后面的人得分减一
}
return 1; //最优情况下没人能比自己高分则可能夺冠
}
int main()
{
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(a+1,a+1+n);
l=1;r=n;
while(l<r)
{
mid=l+r>>1;
if(check(mid))
r=mid;//如果能夺冠,则压缩区间至l~mid
else l=mid+1; //如果mid不能,则压缩区间至mid+1~r
} //这样r一定是答案
printf("%d",n-r+1);
return 0;
}
写得丑,请见谅。