USACO 2017 December Contest Platinum T3: Greedy Gift Takers

Posted li-dox

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了USACO 2017 December Contest Platinum T3: Greedy Gift Takers相关的知识,希望对你有一定的参考价值。

题目大意

有 N(1N1e5)头牛按顺序排成一列,编号从 1 到 N,1 号牛在队头,N 号牛在队尾。

每次位于队头的牛 i 拿到一个礼物,然后插入到从队尾数ci?头牛之前的位置。。举个栗子: 初始队列 1,2,3,4,5 c1?= 2,c2? = 3,则第一次操作后的序列为 2,3,1,4,5,第二次操作后的序列为 3,2,1,4,5。重复无限次操作,求最后有几头牛拿不到礼物。

题目分析

一上来有个显然的结论,若一个人得不到礼物那么原序列中在他后面的人肯定也得不到礼物,因为后面的人跳不到前面来。

所以我们只要找到第一个拿不到礼物的人,计算一下就是答案。

也就是说,我们要找到最后一个能拿到礼物的人。

显然,这个具有单调性,可以二分。

二分可以拿到礼物的人数,那么怎么判断可不可行呢?

我们要判断在 mid之前 是否形成了一个循环即可。若形成了循环,则mid不能拿到礼物。

怎么判断循环呢,出现在前i个位置的牛有多于i头,则这i头牛就会一直卡在这前i个位置。

注意,Check时要把 c[1] ~ c[mid-1] 排序,因为c大的拿礼物一次后可能还会跳到mid前面,而c小的就永远留在后面了,这样相当于在 “c较大的牛“ 的c上减了1。

而让c小的全跳到后面去而不构成循环,才是我们真正要判断的。

 (不理解的可以对着代码仔细想一下)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int MAXN=1e5+10;
 4 
 5 int n,ans;
 6 int c[MAXN],num[MAXN];
 7 inline bool Check(int mid)
 8     if(mid==1) return true;
 9     for(int i=1;i<mid;++i) num[i]=c[i];
10     sort(num+1,num+mid);
11     int lim=n-mid;
12     for(int i=1;i<mid;++i)
13         if(num[i]>lim) return false;
14         ++lim;
15     
16     return true;
17 
18 int main()
19     scanf("%d",&n);
20     for(int i=1;i<=n;++i)
21         scanf("%d",&c[i]);
22     int l=1,r=n+1;
23     while(l<=r)
24         int mid=(l+r)>>1;
25         if(Check(mid))
26             ans=mid;l=mid+1;
27         
28         else
29             r=mid-1;
30     
31     printf("%d\n",n-ans);
32     return 0;
33 

 

以上是关于USACO 2017 December Contest Platinum T3: Greedy Gift Takers的主要内容,如果未能解决你的问题,请参考以下文章

USACO 2017 December Contest Platinum T2: Push a Box

USACO 2017 December Contest Platinum T3: Greedy Gift Takers

USACO 2017 December Contest Gold T1: A Pie for a Pie

USACO 2016 December Contest Gold T1: Moocast

USACO 2007 December Contest, Silver Problem 2. Building Roads Kruskal最小生成树算法

USACO 2018 December Contest Platinum T2: Sort It Out