1. 程式人生 > >常州大學新生寒假訓練會試 大佬的生日大禮包

常州大學新生寒假訓練會試 大佬的生日大禮包

現在做新生題都很艱難了,為啥其他人都覺得很水,給跪了。

考慮給定3種元素個數 a , b , c a,b,c ,如何判斷他們能否形成交錯排列。
為了分析方便,設 a

b c a\ge b\ge c 。首先,將 c c 擺好,那麼最優情況下:

(1) c

c 之間形成的 c 1 c-1 個間隔可以放置 a , b
, a b , a b a , b a b , a b a b a,b,ab,aba,bab,abab

(2)第一個 c c 之前與最後一個 c c 之後可以放置兩段 a b ab 構成的交錯排列。

也就是說,先在(1)中的區間放置一些可選的排列,使得放滿之後剩餘的 a , b a,b 數目滿足 a b 2 |a-b|\le 2 即可。
可以發現,放置 a b , a b a b ab,abab 之類消耗相同元素個數的方案不是最優的,因為他們並不會使得 a b |a-b| 變化,同時也消耗了更多的元素(我們應當儘可能減少元素的消耗防止在某個區間沒有元素可放)。同樣的,放置 a b a , b a b aba,bab 也不是最優的。那麼最後的方案就很顯然了,即先依次放置 a b 2 a-b-2 a a ,接著交替放置 a a b b 即可,容易證明這是一定滿足要求的。
故我們可以得到,對於給定元素個數 a , b , c a,b,c ,如果他們能夠形成交錯排列,則: c a b 1 c\ge a-b-1

回到問題,在二分時我們需要判斷:對於給定的 m m ,是否存在 a A , b B , c C a\le A,b\le B,c\le C ,使得 a + b + c = m a+b+c=m 且他們能夠形成交錯排列。
考慮直接用 A , B , C A,B,C 進行判斷:
如果 C A B 1 C\ge A-B-1 ,那麼我們將他們形成的交錯排列取掉 C m C-m 個字首元素或者字尾元素即可;
否則,我們將 A A 減掉 A B ( C + 1 ) A-B-(C+1) ,使得其滿足要求。容易證明此時是最優的情況。那麼如果 A + B + C A + B + C + 1 = 2 ( B + C ) + 1 m A+B+C-A+B+C+1=2(B+C)+1\ge m ,則存在我們需要的交錯排列。

二分即可。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<climits>
#include<random>
using namespace std;
//--Container
//--
#define clr(a) memset(a,0,sizeof(a))
typedef long long ll;
int ar[3];
bool _cl(int m){
    int tr[3]={ar[0]-m,ar[1]-m,ar[2]};
    if(tr[0]<0||tr[1]<0||tr[0]+tr[1]+tr[2]<m)return 0;
    sort(tr,tr+3);
    if(tr[0]>=tr[2]-tr[1]-1)return 1;
    if(tr[0]*2+tr[1]*2+1<m)return 0;
    return 1;
};
 
void cl(){
    int b,e,m,d;scanf("%d %d %d",&ar[0],&ar[1],&ar[2]);
    for(b=0,e=3000000;b<=e;){
        m=(b+e)>>1;
        if(_cl(m))d=m,b=m+1;
        else
            e=m-1;
    }
    printf("%d\n",d);
};
 
int main(){
#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
#endif // ONLINE_JUDGE
    int t;scanf("%d",&t);while(t--)cl();
    return 0;
};