长城守卫(Beijing Guards,CERC 2004,LA3177)

AC通道:
https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1178


长城守卫


Description

n 个人围成一个圈,其中第 i 个人想要有 ri 个不同的礼物。
相邻的两个人可以聊天,炫耀自己的礼物。

如果两个相邻的人拥有同一种礼物,则双方都会很不高兴。

问:一共需要多少种礼物才能满足所有人的需要?

假设每种礼物有无穷多个,不相邻的两个人不会一起聊天,所以即使拿到相同的礼物也没关系。

比如,一共有 5 个人,每个人都要一个礼物,则至少要 3 种礼物。
如果把这 3 种礼物编号为 123 ,则5个人拿到的礼物应分别是: 12123

如果每个人要两个礼物,则至少要 5 种礼物,且 5 个人拿到的礼物集合应该是:
{12}{34}{15}{23}{45}


Input

输入包含多组数据。每组数据的第一行为一个整数 n
以下 n 行按照圈上的顺序描述每个人的需求,其中每行为一个整数 ri ,表示第 i 个人想要 ri 个不同的礼物。
输入结束标志为 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% 的数据, 1n105 1ri105


Solution

为了解题方便,我们假设给第 1 个人的礼物为 1 ~ r1

n 为偶数时,可以知道,若答案为 p ,那么可以给编号为偶数的人(编号为 i )取礼物 pri+1 ~ p ,编号为奇数的人(编号为 i )取礼物 1 ~ ri

此时,因为 ri1<pri+1 (编号为奇数的人取的礼物的右端必须小于编号为偶数的人取的礼物的左端),且 ri1 pri 都为整数,所以有 ri1pri ,即 ri1+rip ,且 p 要最小。

所以 p=Max{ri+ri+1} rn+1=r1

我们发现, p 为答案的下限 —— 若 ans<Max{ri+ri+1} ,则礼物不够相邻的两个人拿。

所以当 n 为偶数时, p=Max{ri+ri+1} rn+1=r1

n 为奇数时,我们可以这样考虑:

因为编号为 n 的人取的礼物要和编号为 1 的人取的礼物不同,所以我们让编号为 n 的人取的礼物尽量靠右。

如何让他取的礼物尽量靠右呢?就必须要在编号为 n1 的人取的礼物尽量靠左的前提下,他尽量往右取礼物。

如何让编号为 n1 的人取的礼物尽量靠左呢?就必须要在编号为 n2 的人取的礼物尽量靠右的前提下,他尽量往右取礼物。

……

依次类推,我们可以得到一个规律:
编号为偶数的人尽量往前取,编号为奇数的人尽量往后取(编号为 1 的人取的礼物已经规定了)。

因为要使一些人取的礼物尽量靠右,所以我们需要二分枚举右端点,即礼物的种类数。

我们只需模拟这个过程,然后判断编号为 1 的人和编号为 n 的人是否冲突即可。

我们可以记录每个人在 [1r1] 的范围内取了几个和在 [r1+1n] 的范围里取了几个,最后判断第 n 个人在 [1r1] 的范围内是否取了即可。

二分的下界我们之前已经讨论过了,即前文的 p
二分的上界我们可以设置为 ni=1ri

Code

[cpp]
  1. #include   
  2. #include   
  3. #include   
  4.   
  5. using namespace std;  
  6.   
  7. int r[100010];  
  8. int le[100010],ri[100010];  
  9. int n;   
  10. int maxn;  
  11. long long tmp,tmp2;  
  12.   
  13. inline int Max(int x,int y){  
  14.     return x>y?x:y;  
  15. }  
  16.   
  17. inline int Min(int x,int y){  
  18.     return x
  19. }  
  20.   
  21. bool pan(long long s){  
  22.     memset(ri,0,sizeof ri);  
  23.     memset(le,0,sizeof le);   
  24.     le[1]=r[1];  
  25.     for(int i=2;i<=n;i++){  
  26.         if(!(n&1)){  
  27.             le[i]=Min(r[i],r[1]-le[i-1]);  
  28.             ri[i]=r[i]-le[i];   
  29.         }  
  30.         else{  
  31.             ri[i]=Min(r[i],(s-r[1])-ri[i-1]);  
  32.             le[i]=r[i]-ri[i];  
  33.         }  
  34.     }     
  35.     return (!le[n]);  
  36. }  
  37.   
  38. int main(){  
  39.     while(scanf(“%d”,&n)!=EOF&&n){  
  40.         maxn=0;  
  41.         int t=0,ans;  
  42.         for(int i=1;i<=n;i++){  
  43.             scanf(”%d”,&r[i]);  
  44.             maxn=Max(r[i]+r[i-1],maxn);  
  45.             t+=r[i];  
  46.         }  
  47.         maxn=Max(maxn,r[n]+r[1]);  
  48.         if(!(n&1)){  
  49.             printf(”%d\n”,maxn);  
  50.             continue;  
  51.         }  
  52.         ans=t;  
  53.         int s=maxn;  
  54.         while(s<=t){  
  55.             long long mid=(s+t)/2;  
  56.             bool flag=pan(mid);  
  57.             if(flag){  
  58.                 if(ans>mid)ans=mid;  
  59.                 if(t!=mid-1)t=mid-1;  
  60.                 else break;  
  61.             }  
  62.             else{  
  63.                 if(s!=mid+1)s=mid+1;  
  64.                 else break;  
  65.             }  
  66.         }  
  67.         printf(”%d\n”,ans);  
  68.     }  
  69.     return 0;  
  70. }  

你可能感兴趣的:(题解,二分与补集转化,LA)