长城守卫
Description
有
n
个人围成一个圈,其中第
相邻的两个人可以聊天,炫耀自己的礼物。
如果两个相邻的人拥有同一种礼物,则双方都会很不高兴。
问:一共需要多少种礼物才能满足所有人的需要?
假设每种礼物有无穷多个,不相邻的两个人不会一起聊天,所以即使拿到相同的礼物也没关系。
比如,一共有
5
个人,每个人都要一个礼物,则至少要
如果把这
3
种礼物编号为
如果每个人要两个礼物,则至少要
5
种礼物,且
{1,2},{3,4},{1,5},{2,3},{4,5}
。
Input
输入包含多组数据。每组数据的第一行为一个整数
n
;
以下
输入结束标志为
n=0
。
Output
对于每组数据,输出所需礼物的种类数。
Sample Input
3
4
2
2
5
2
2
2
2
2
5
1
1
1
1
1
0
Sample Output
8
5
3
HINT
对于 100% 的数据, 1≤n≤105 , 1≤ri≤105 。
Solution
为了解题方便,我们假设给第
1
个人的礼物为
当
n
为偶数时,可以知道,若答案为
此时,因为 ri−1<p−ri+1 (编号为奇数的人取的礼物的右端必须小于编号为偶数的人取的礼物的左端),且 ri−1 与 p−ri 都为整数,所以有 ri−1≤p−ri ,即 ri−1+ri≤p ,且 p 要最小。
所以
我们发现,
p
为答案的下限 —— 若
所以当
n
为偶数时,
当 n 为奇数时,我们可以这样考虑:
因为编号为
如何让他取的礼物尽量靠右呢?就必须要在编号为 n−1 的人取的礼物尽量靠左的前提下,他尽量往右取礼物。
如何让编号为 n−1 的人取的礼物尽量靠左呢?就必须要在编号为 n−2 的人取的礼物尽量靠右的前提下,他尽量往右取礼物。
……
依次类推,我们可以得到一个规律:
编号为偶数的人尽量往前取,编号为奇数的人尽量往后取(编号为
1
的人取的礼物已经规定了)。
因为要使一些人取的礼物尽量靠右,所以我们需要二分枚举右端点,即礼物的种类数。
我们只需模拟这个过程,然后判断编号为
我们可以记录每个人在
二分的下界我们之前已经讨论过了,即前文的
p
;
二分的上界我们可以设置为
Code
- #include <iostream>
- #include <cstdio>
- #include <cstring>
- using namespace std;
- int r[100010];
- int le[100010],ri[100010];
- int n;
- int maxn;
- long long tmp,tmp2;
- inline int Max(int x,int y){
- return x>y?x:y;
- }
- inline int Min(int x,int y){
- return x<y?x:y;
- }
- bool pan(long long s){
- memset(ri,0,sizeof ri);
- memset(le,0,sizeof le);
- le[1]=r[1];
- for(int i=2;i<=n;i++){
- if(!(n&1)){
- le[i]=Min(r[i],r[1]-le[i-1]);
- ri[i]=r[i]-le[i];
- }
- else{
- ri[i]=Min(r[i],(s-r[1])-ri[i-1]);
- le[i]=r[i]-ri[i];
- }
- }
- return (!le[n]);
- }
- int main(){
- while(scanf(“%d”,&n)!=EOF&&n){
- maxn=0;
- int t=0,ans;
- for(int i=1;i<=n;i++){
- scanf(”%d”,&r[i]);
- maxn=Max(r[i]+r[i-1],maxn);
- t+=r[i];
- }
- maxn=Max(maxn,r[n]+r[1]);
- if(!(n&1)){
- printf(”%d\n”,maxn);
- continue;
- }
- ans=t;
- int s=maxn;
- while(s<=t){
- long long mid=(s+t)/2;
- bool flag=pan(mid);
- if(flag){
- if(ans>mid)ans=mid;
- if(t!=mid-1)t=mid-1;
- else break;
- }
- else{
- if(s!=mid+1)s=mid+1;
- else break;
- }
- }
- printf(”%d\n”,ans);
- }
- return 0;
- }