003.二分

二分

Although the basic idea of binary search is comparatively straightforward, the details can be surprisingly tricky...

思路很简单,细节是魔鬼

二分查找

在给定有序数组中查找是否存在某元素

  • 存在:返回下标
  • 不存在:返回-1
int binarySearch(vector<int>&nums,int target){
    int l=0,r=nums.size()-1,mid;
    while(l<=r){
        mid=(l+r)>>1;//位运算:二进制下右移一位,等价于mid=(l+r)/2
        if(nums[mid]==target)return mid;
        else if(nums[mid]>target)r=mid-1;
        else l=mid+1;
    }
    return -1;
}

在给定数组(不一定有序)中查找是否存在某元素

  • 存在:返回1
  • 不存在:返回0
bool binarySearch(vector<int>&nums,int target){
    sort(nums.begin(),nums.end());
    int l=0,r=nums.size()-1,mid;
    while(l<=r){
        mid=(l+r)>>1;
        if(nums[mid]==target)return 1;
        else if(nums[mid]>target)r=mid-1;
        else l=mid+1;
    }
    return 0;
}
//为了避免(l+r)造成的整型溢出
//优化:mid=l+(r-l)>>1

二分的应用依赖有序性

基于有序性,查找失败后可以缩小一半的查找范围,实现O(logn)查找


抽象有序性

给定一个bool数组[0,0,0,0,0,0,0,1,1,1,1,1,1]

要求返回第一个1的下标

题目保证存在至少一个1

int solve(vector<bool>&abstract){
    int l=0,r=abstract.size()-1,mid,ans;
    while(l<=r){
        mid=l+(r-l)>>1;
        if(abstract[mid])ans=mid,r=mid-1;
        else l=mid+1;
    }
    return ans;
}

同理我们可以找到最后一个0的下标

如果把bool数组反过来[1,1,1,1,1,1,0,0,0,0,0,0,0,0,0]也不在话下,只需更改判断逻辑就可以了

我们如果遇到这样的问题:

  • 存在某种有序性(可能十分隐蔽)
  • 求临界条件(一般是求满足条件的最大的最小值,最小的最大值

一般都可以抽象出一个bool函数

模板如下

bool check(int x){
    …………
}
void solve(){
    //[min_ans,max_ans]:初始查找范围
    int l=min_ans,r=max_ans,mid,ans;
    while(l<=r){
        mid=l+(r-l)/2;
        if(check(mid))ans=mid,…………;
        else …………;
    }
    printf("%d\n",ans);
}

根据题目逻辑实现check函数范围修改

一道例题:序列划分

题目描述:

给定n个正整数a_1,a_2,……,a_n,将这个序列从左到右划分成m段,使得每段至少有一个数。

你需要让数字之和最大的那一段的数字和尽可能得小。

Input

第一行包含一个正整数T(1<=T<=10),表示测试数据的组数。

每组数据第一行包含两个正整数n,m(1<= m<= n<= 100000)。

第二行包含n个正整数a_1,a_2,……,a_n(1<= a_i<= 10^9)。

Output

对于每组数据输出一行一个整数,即你找到的方案中,数字之和最大的那一段的数字和。

Sample Input

1

6 4

1 5 4 6 2 3

Sample Output

6

思路:

  1. 发现单调性:

    设答案为ans。随着ans减小,能分得的最大段数增多(不减少)

  2. 抽象bool函数:

    段数小于等于m时合法(数字之和小于ans的一段分成的两段的数字之和仍然小于ans,这意味着当段数小于m时可以通过分裂操作将段数调整至m,这是合法的)

    段数大于m时非法

  3. 确定初始范围:

    ans不小于a[1,2,……,n]的最大值(m=n时)

    ans不大于a[1,2,……,n]的总和(m=1时)

  4. 设计check函数:

    简单贪心:

    从左到右累加,一旦累加和大于ans就把当前元素当作下一个区间的第一个元素

    O(n)算出ans情况下能分得的最大段数

  5. 二分答案:

    套模板:

    O(nlog(n))找到最小的ans

//观察数据范围,记得开long long
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=100005;
int n,m;
ll a[MAXN],min_ans,max_ans;
bool check(ll x){
    ll cnt=1;
    ll cur_sum=0;
    for(int i=0;i<n;++i){
        if(cur_sum+a[i]<=x){
            cur_sum+=a[i];
        }
        else{
            cur_sum=a[i];
            cnt++;
        }
    }
    return cnt<=m;
} 
void solve(){
    ll l=min_ans,r=max_ans,mid,ans=r;
    while(l<=r){
        mid=l+(r-l)/2;
        if(check(mid))ans=mid,r=mid-1;
        else l=mid+1;
    }
    cout<<ans<<'\n';
}
int main(){
    cin.tie(0)->sync_with_stdio(0);
    int T;
    cin>>T;
    while(T--){
        cin>>n>>m;
        min_ans=-1;
        max_ans=0;
        for(int i=0;i<n;++i){
            cin>>a[i];
            max_ans+=a[i];
            if(a[i]>min_ans)min_ans=a[i];
        }
        solve();
    }
}

练习题

前5题涉及浮点数精度

HDU1551

code
//分蛋糕问题
//浮点数二分时需注意:
//循环终止条件为:r-l<1e-9(或者别的精度)
//缩小范围时使用l=mid或l=mid+1e-9,而不是l=mid+1(r同理)
#include<bits/stdc++.h>
using namespace std;
#define ll  long long
#define pii pair<int,int>
#define MOD 1000000007
#define INF 0x3f3f3f3f
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
const int MAXN=10005;
int n,k;
double M;
double a[MAXN];
bool check(double x){
    int cnt=0;
    for(int i=0;i<n;++i)cnt+=(int)(a[i]/x);
    return cnt>=k;
}
void solve(){
    double l=0,r=M,mid;
    while(r-l>1e-5){
        mid=(l+r)/2;
        if(check(mid))l=mid;
        else r=mid;
    }
    printf("%.2lf\n",l);
}
signed main(){
    //cin.tie(0)->sync_with_stdio(0);
    //int T=read();
    while(scanf("%d%d",&n,&k)){
        if(n==0&&k==0)break;
        M=0;
        for(int i=0;i<n;++i){
            scanf("%lf",a+i);
            M+=a[i];
        }
        M/=k;
        solve();
    }
}

HDU1969

code
//与1551类似
//涉及圆周率:
//const double PI=acos(-1.0)或者const double PI=3.1415926(精度一般够用);
#include<bits/stdc++.h>
using namespace std;
#define ll  long long
#define pii pair<int,int>
#define MOD 1000000007
#define INF 0x3f3f3f3f
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
const int MAXN=10005;
const double pi=acos(-1.0);
const double e=1e-5;
int n,f,R[MAXN],MR;
bool check(double x){
    int cnt=0;
    for(int i=0;i<n;++i){
        cnt+=(int)(pi*R[i]*R[i]/x);
    }
    return cnt>f;
}
void solve(){
    double l=0.0,r=pi*MR*MR;
    while(r-l>e){
        double mid=(l+r)/2.0;
        if(check(mid))l=mid;
        else r=mid;
    }
    printf("%.4lf\n",l);
}
signed main(){
    //cin.tie(0)->sync_with_stdio(0);
    int T=read();
    while(T--){
        n=read(),f=read();
        MR=0;
        for(int i=0;i<n;++i){
            R[i]=read();
            if(R[i]>MR)MR=R[i];
        }
        solve();
    }
}

HDU2199

code
//函数在[0,100]明显单调
#include<bits/stdc++.h>
using namespace std;
#define ll  long long
#define pii pair<int,int>
#define MOD 1000000007
#define INF 0x3f3f3f3f
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
const int MAXN=100005;
double Y;
inline double f(double x){
    return 8*x*x*x*x+7*x*x*x+2*x*x+3*x+6;
}
void solve(){
    if(f(0)>Y||f(100)<Y){
        printf("No solution!\n");
        return ;
    }
    double l=0.0,r=100.0,mid,ans;
    while(r-l>1e-6){
        mid=(l+r)/2.0;
        if(Y-f(mid)<=1e-5)ans=mid,r=mid;
        else l=mid;
    }
    printf("%.4lf\n",ans);
}
signed main(){
    //cin.tie(0)->sync_with_stdio(0);
    int T=read();
    while(T--){
        scanf("%lf",&Y);
        solve();
    }
}

HDU2289

code
//涉及简单物理知识
//cup应该是上粗下细的
//利用相似求出实际上表面半径
//台体体积=1/3*h*(S+s+sqrt(S*s))
#include<bits/stdc++.h>
using namespace std;
#define ll  long long
#define pii pair<int,int>
#define MOD 1000000007
#define INF 0x3f3f3f3f
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
const int MAXN=100005;
const double pi=acos(-1.0);
double r,R,H,V;
void solve(){
    double left=0,right=H,mid;
    while(right-left>1e-9){
        mid=(left+right)/2;
        double r1=(R-r)*mid/H+r;
        double v=pi/3*mid*(r*r+r1*r1+r*r1);
        if(fabs(v-V)<=1e-9)break ;
        else if(v>V)right=mid-1e-9;
        else left=mid+1e-9;
    }
    printf("%.6lf\n",mid);
}
signed main(){
    //cin.tie(0)->sync_with_stdio(0);
    int T=read();
    while(T--){
        scanf("%lf %lf %lf %lf",&r,&R,&H,&V);
        solve();
    }
}

HDU2298

code
//中学物理题
//设角度为t,运动时间为T
//分解初速度:水平v*cos(t),竖直v*sin(t)
//分解运动:
//水平匀速直线:x=v*cos(t)*T
//竖直匀减速:y=v*sin(t)*T-g/2*T*T
//消去T
//y=x*tan(t)-(g*x*x)/(2*v*v*cos(t)*cos(t))
//现在已知y,x
//二分试出t即可
#include<bits/stdc++.h>
using namespace std;
#define ll  long long
#define pii pair<int,int>
#define MOD 1000000007
#define INF 0x3f3f3f3f
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
const int MAXN=100005;
const double pi=acos(-1.0);
const double e=1e-9;
const double g=9.80;
double x,y,v;
void solve(){
    double tant=v*v/(g*x);
    double M=atan(tant);
    double cost=cos(M);
    double Ymax=x*tant-g*x*x/(2.0*v*v*cost*cost);
    if(Ymax<y){
        printf("-1\n");
        return ;
    }
    double l=0,r=M,mid;
    while(r-l>e){
        mid=(l+r)/2;
        double tanm=tan(mid);
        double cosm=cos(mid);
        double Ym=x*tanm-g*x*x/(2.0*v*v*cosm*cosm);
        if(Ym>=y)r=mid;
        else l=mid;
    }
    printf("%.6lf\n",l);
}
signed main(){
    //cin.tie(0)->sync_with_stdio(0);
    int T=read();
    while(T--){
        scanf("%lf%lf%lf",&x,&y,&v);
        solve();
    }
}

HDU2141

code
//直接暴力是O(n^3)
//类似哈希优化,不过我们手动二分查找
//实现O(n^2+nlog(n*n))
#include<bits/stdc++.h>
using namespace std;
#define ll  long long
#define pii pair<int,int>
#define MOD 1000000007
#define INF 0x3f3f3f3f
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
const int MAXN=505;
int L,N,M,S;
int A[MAXN],B[MAXN],C[MAXN],x;
int AB[MAXN*MAXN];
bool check(int x){
    int l=0,r=L*N-1;
    while(l<=r){
        int mid=(l+r)>>1;
        if(AB[mid]==x)return 1;
        else if(AB[mid]<x)l=mid+1;
        else r=mid-1;
    }
    return 0;
}
void solve(){
    for(int i=0;i<M;i++){
        int tar=x-C[i];
        if(check(tar)){
            printf("YES\n");
            return ;
        }
    }
    printf("NO\n");
}
signed main(){
    //cin.tie(0)->sync_with_stdio(0);
    //int T=read();
    int tot=0;
    while(scanf("%d %d %d",&L,&N,&M)==3){
        printf("Case %d:\n",++tot);
        for(int i=0;i<L;i++)A[i]=read();
        for(int i=0;i<N;i++)B[i]=read();
        for(int i=0;i<M;i++)C[i]=read();
        int p=0;
        for(int i=0;i<L;i++){
            for(int j=0;j<N;j++){
                AB[p++]=A[i]+B[j];
            }
        }
        sort(AB,AB+L*N);
        S=read();
        while(S--){
            x=read();
            solve();
        }

    }
}

HDU4004

code
//手动添加3块石头
//d[0]:初始位置
//d[n+1]:终点
//d[n+2]:无穷远
#include<bits/stdc++.h>
using namespace std;
#define ll  long long
#define pii pair<int,int>
#define MOD 1000000007
#define INF 0x7fffffff
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
const int MAXN=500005;
int L,n,m;
int d[MAXN];
int f(int x){
    int cnt=0;
    int i=0,j=0;
    while(i<n+1){
        while(j<=n+1&&d[j+1]-d[i]<=x)j++;
        cnt++;
        i=j;
    }
    return cnt;
}
signed main(){
    //cin.tie(0)->sync_with_stdio(0);
    //int T=read();
    while(scanf("%d%d%d",&L,&n,&m)==3){
        for(int i=1;i<=n;i++)scanf("%d",d+i);
        sort(d+1,d+n+1);
        d[0]=0;
        d[n+1]=L;
        d[n+2]=INF;
        int max_d=0;
        for(int i=1;i<=n+1;i++){
            if(d[i]-d[i-1]>max_d)max_d=d[i]-d[i-1];
        }
        int l=max_d,r=L;
        int ans=r;
        while(l<=r){
            int mid=l+(r-l)/2;
            if(f(mid)<=m)ans=mid,r=mid-1;
            else l=mid+1;
        }
        printf("%d\n",ans);
    }
}

HDU4190

code
//与分蛋糕类而不同
//区别在于这里我们需要向上取整
#include<bits/stdc++.h>
using namespace std;
#define ll  long long
#define pii pair<int,int>
#define MOD 1000000007
#define INF 0x3f3f3f3f
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
const int MAXN=500005;
int n,b,a[MAXN],M;
bool check(int x){
    int cnt=0;
    for(int i=0;i<n;++i){
        cnt+=a[i]/x;
        if(a[i]%x!=0)cnt++;
    }
    return cnt<=b;
}
void solve(){
    int l=1,r=M,mid,ans=l;
    while(l<=r){
        mid=(l+r)>>1;
        if(check(mid))ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%d\n",ans);
}
signed main(){
    //cin.tie(0)->sync_with_stdio(0);
    //int T=read();
    while(scanf("%d%d",&n,&b)==2&&n!=-1){
        M=0;
        for(int i=0;i<n;i++){
            a[i]=read();
            if(M<a[i])M=a[i];
        }
        solve();
    }
}

HDU4430

code
//n<=1e12
//因为k>=2,所以r最多不超过40
//枚举r,二分查找合法的k
//注意:中心可以插1个或0个蜡烛
#include<bits/stdc++.h>
using namespace std;
#define ll  long long
#define pii pair<int,int>
#define MOD 1000000007
#define INF 0x3f3f3f3f
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
const int MAXN=100005;
ll n;
void solve(){
    ll r=1,k=n-1;
    for(int i=2;i<=40;i++){
        ll kl=2,kr=n;
        while(kr>=kl){
            ll km=kl+(kr-kl)/2;
            ll sum=0;
            ll t=1;
            for(int j=1;j<=i;j++)
            {
                if(sum>n)break;
                if(n/t<km){sum=n+1;break;}
                t*=km;
                sum+=t;
            }
            if(sum==n||sum==n-1){
                if((km*i<r*k)||(km*i==r*k&&i<r))k=km,r=i;
                break;
            }
            if(sum>n)kr=km-1;
            else kl=km+1;
        }
    }
    printf("%lld %lld\n",r,k);
}
signed main(){
    while(scanf("%lld",&n)!=EOF){
        solve();
    }
}

HDU5248

code
//贪心:左边的元素越小越有利
#include<bits/stdc++.h>
using namespace std;
#define ll  long long
#define pii pair<int,int>
#define MOD 1000000007
#define INF 0x3f3f3f3f
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
const int MAXN=100005;
int n,A[MAXN],B[MAXN];
bool check(int x){
    for(int i=1;i<=n;i++)B[i]=A[i];
    B[0]=-x;
    for(int i=1;i<=n;i++){
        if(B[i]>B[i-1]){
            if(B[i]-B[i-1]-1<=x)B[i]=B[i-1]+1;
            else B[i]-=x;
        }
        else{
            if(B[i-1]+1-B[i]<=x)B[i]=B[i-1]+1;
            else return 0;
        }
    }
    return 1;
}
void solve(){
    int l=0,r=1e6;
    int ans=r;
    while(l<=r){
        int mid=(l+r)>>1;
        if(check(mid))ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%d\n",ans);
}
signed main(){
    //cin.tie(0)->sync_with_stdio(0);
    int T=read();
    int tot=0;
    while(T--){
        printf("Case #%d:\n",++tot);
        n=read();
        for(int i=1;i<=n;++i)A[i]=read();
        solve();
    }
}

HDU5884

code
//每次合并操作相当于减少了k-1个元素,最后仅剩1个元素
//让较小的元素合并尽可能多次
//为方便操作,我们添加若干0元素,使得恰好每次合并k个元素,最后剩余一个,同时不影响cost
//最开始我是使用一个priority_queue(二叉堆),但push,pop都是O(logn)的
//而我们提前排序后从左往右合并天然满足递增
//可以再开一个数组存放合并后的新值
//每次合并时保证合并的是目前最小元素即可
#include<bits/stdc++.h>
using namespace std;
#define ll  long long
#define pii pair<int,int>
#define MOD 1000000007
#define INF 0x3f3f3f3f
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
const int MAXN=100005;
int n,t,a[MAXN];
bool check(int x){
    ll cost=0;
    queue<ll>q1,q2;
    int zeros=x-1-(n-1)%(x-1);
    for(int i=0;i<zeros;i++)q1.push(0);
    for(int i=0;i<n;i++)q1.push(a[i]);
    while(q1.size()+q2.size()>1){
        ll sum=0;
        for(int i=0;i<x;i++){
            if(!q1.empty()&&!q2.empty()){
                if(q1.front()<q2.front())sum+=q1.front(),q1.pop();
                else sum+=q2.front(),q2.pop();
            }
            else if(!q2.empty())sum+=q2.front(),q2.pop();
            else if(!q1.empty())sum+=q1.front(),q1.pop();
            else break;
        }
        cost+=sum;
        q2.push(sum);
    }
    return cost<=t;
}
void solve(){
    int l=2,r=n,mid,ans=r;
    while(l<=r){
        mid=(l+r)>>1;
        if(check(mid))ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%d\n",ans);
}
signed main(){
    //cin.tie(0)->sync_with_stdio(0);
    int T=read();
    while(T--){
        n=read(),t=read();
        for(int i=0;i<n;++i)a[i]=read();
        sort(a,a+n);
        solve();
    }
}

HDU6231

code
//通过copy一个有序数组A,实现尺取
#include<bits/stdc++.h>
using namespace std;
#define ll  long long
#define pii pair<int,int>
#define MOD 1000000007
#define INF 0x3f3f3f3f
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
const int MAXN=100005;
int n,k,m;
int a[MAXN],A[MAXN];
bool check(int x){
    int sum=0,cnt=0;
    int r=0;
    for(int l=0;l<n;++l){
        while(r<n&&cnt<k){
            if(a[r]>=x)cnt++;
            r++;
        }
        if(cnt>=k)sum+=n-r+1;
        if(sum>=m)return 1;
        if(a[l]>=x)cnt--;
    }
    return sum>=m;
}
void solve(){
    int l=0,r=n-1,mid,ans=l;
    while(l<=r){
        mid=(l+r)>>1;
        if(check(A[mid]))ans=A[mid],l=mid+1;
        else r=mid-1;
    }
    printf("%d\n",ans);
}
signed main(){
    int T=read();
    while(T--){
        n=read(),k=read(),m=read();
        for(int i=0;i<n;++i)a[i]=read(),A[i]=a[i];
        sort(A,A+n);
        solve();
    }
}

HDU6288

code
//预处理出log2向上取整的值
//技巧:比较n^a(⌈log2n⌉)^b与k,为避免乘法溢出
//我们用k除以a个n,b个log2n,判断结果与1的关系
#include<bits/stdc++.h>
using namespace std;
#define ll  long long
#define pii pair<int,int>
#define MOD 1000000007
#define INF 0x3f3f3f3f
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
const int MAXN=100005;
int a,b;
ll k;
ll s[100]={1};
bool check(ll x){
    ll j;
    for(j=0;j<=62;j++)if(x<=s[j])break;
    long double t=k;
    for(int i=0;i<a;i++){
        t/=(long double)x;
        if(t<1.0)return 0;
    }
    for(int i=0;i<b;i++){
        t/=(long double)j;
        if(t<1.0)return 0;
    }
    return 1;
}
void solve(){
    ll l=0,r=k,mid,ans=l;
    while(l<=r){
        mid=(l+r)>>1;
        if(check(mid))ans=mid,l=mid+1;
        else r=mid-1;
    }
    printf("%lld\n",ans);
}
signed main(){
    //cin.tie(0)->sync_with_stdio(0);
    int T=read();
    for(int i=1;i<=62;i++)s[i]=(ll)(1ll<<i);
    while(T--){
        a=read(),b=read();
        scanf("%lld",&k);
        solve();
    }
}

HDU6383

code
//最大的最小值
//每次操作a-2,b+1,总和-1
//注意:我们可以实现对一个元素的-1操作(先-2,再+1)
//二分最小值,check能否合法地变成稳定数组
//check的标准:使所有元素不小于min,也就是将所有比mid小的元素x变成不小于min的数,所需的最小up(+1)次数为(min-x)
//我们统计一共最少需要多少次up操作,最多可以实现多少次down(-2)操作
//满足:如果down<up,说明无法合法达到稳定
//down==up,正好
//down>up,我们只需up次down操作,剩下的down不进行,不影响数组的稳定性
//注意:我们对一个元素-1(-2+1)进行了一次down,一次up,不影响最后down与up的比较,与就是说我们无需考虑恰好比min大1的元素
#include<bits/stdc++.h>
using namespace std;
#define ll  long long
#define pii pair<int,int>
#define MOD 1000000007
#define INF 0x3f3f3f3f
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
const int MAXN=300005;
int n;
ll x[MAXN],M,m;
bool check(ll mid){
    ll down=0,up=0;
    for(int i=0;i<n;i++){
        if(mid>x[i])up+=mid-x[i];
        else if(x[i]>mid+1) down+=(x[i]-mid)/2;
    }
    return down>=up;
}
void solve(){
    ll l=m,r=M,mid,ans;
    while(r>=l){
        mid=(l+r)>>1;
        if(check(mid))ans=mid,l=mid+1;
        else r=mid-1;
    }
    printf("%lld\n",ans);
}
signed main(){
    int T=read();
    while(T--){
        n=read();
        M=0;
        m=1e8;
        for(int i=0;i<n;++i){
            scanf("%lld",x+i);
            if(x[i]>M)M=x[i];
            if(x[i]<m)m=x[i];
        }
        solve();
    }
}

leetcode410

code
//例题
// class Solution {
// public:
//     int splitArray(vector<int>& nums, int k) {
//         int n=nums.size();
//         int sum=0,max=0;
//         for(int w:nums){
//             sum+=w;
//             if(w>max)max=w;
//         }
//         int l=max,r=sum;
//         int ans=r;
//         while(l<=r){
//             int mid=(l+r)>>1;
//             if(f(mid,nums)<=k)ans=mid,r=mid-1;
//             else l=mid+1;
//         }
//         return ans;
//     }
//     int f(int x,vector<int>&nums){
//         int cnt=1;
//         int sum=0;
//         for(int w:nums){
//             if(sum+w<=x){
//                 sum+=w;
//             }
//             else{
//                 cnt++;
//                 sum=w;
//             }
//         }
//         return cnt;
//     }
// };

leetcode719

code
//二分+滑动窗口
// class Solution {
//     int n;
// public:
//     int smallestDistancePair(vector<int>& nums, int k) {
//         n=nums.size();
//         sort(nums.begin(),nums.end());
//         int l=0,r=nums[n-1]-nums[0],mid,ans;
//         while(l<=r){
//             mid=(l+r)>>1;
//             if(check(mid,nums,k))ans=mid,r=mid-1;
//             else l=mid+1;
//         }
//         return ans;
//     }
//     bool check(int x,vector<int>&nums,int k){
//         int cnt=0;
//         int i=0,j=0;
//         for(;i<n;i++){
//             while(j+1<n&&nums[j+1]-nums[i]<=x)j++;
//             cnt+=(j-i);
//         }
//         return cnt>=k;
//     }
// };

leetcode875

code
// class Solution {
// public:
//     int minEatingSpeed(vector<int>& piles, int h) {
//         int n=piles.size();
//         int l=1,r=1e9+1;
//         int ans=l;
//         while(l<=r){
//             int mid=(l+r)>>1;
//             int cnt=0;
//             for(int ba:piles){
//                 cnt+=ba/mid;
//                 if(ba%mid)cnt++;
//                 if(cnt>h)break;
//             }
//             if(cnt<=h)ans=mid,r=mid-1;
//             else l=mid+1;
//         }
//         return ans;
//     }
// };

leetcode1011

code
// class Solution {
// public:
//     int f(int x,vector<int>&wight){
//         int cnt=1;
//         int N=x;
//         for(int w:wight){
//             if(N>=w){
//                 N-=w;
//             }
//             else{
//                 cnt++;
//                 N=x-w;
//             }
//         }
//         return cnt;
//     }
//     int shipWithinDays(vector<int>& weights, int days) {
//         int n=weights.size();
//         int sum=0,max=0;
//         for(int w:weights){
//             sum+=w;
//             if(w>max)max=w;
//         }
//         int l=max,r=sum;
//         int ans=r;
//         while(l<=r){
//             int mid=(l+r)>>1;
//             if(f(mid,weights)<=days)ans=mid,r=mid-1;
//             else l=mid+1;
//         }
//         return ans;
//     }
// };

leetcode2141

code
//记得开long long

// class Solution {
// public:
//     long long maxRunTime(int n, vector<int>& batteries) {
//         long long sum=0;
//         int m=1e9;
//         for(int t:batteries){
//             if(t<m)m=t;
//             sum+=(long long)t;
//         }
//         long long l=(long long)m,r=sum,mid,ans;
//         while(l<=r){
//             mid=(l+r)>>1;
//             if(check(mid,n,batteries))ans=mid,l=mid+1;
//             else r=mid-1;
//         }
//         return ans;
//     }
//     bool  check(long long x,int n,vector<int>&batteries){
//         long long sum=0;
//         for(int t:batteries){
//             if((long long)t>=x)sum+=x;
//             else sum+=(long long)t;
//         }
//         return sum>=n*x;
//     }
// };

leetcode2187

code
// class Solution {
// public:
//     long long minimumTime(vector<int>& time, int totalTrips) {
//         if(time.size()==1)return (long long)time[0]*totalTrips;
//         int m=1e7;
//         for(int t:time)if(t<m)m=t;
//         long long l=(long long)m,r=(long long)m*(long long)totalTrips,mid,ans;
//         while(l<=r){
//             mid=(l+r)>>1;
//             if(check(mid,time,totalTrips))ans=mid,r=mid-1;
//             else l=mid+1;
//         }
//         return ans;
//     }
//     bool check(long long x,vector<int>&time,int k){
//         long long cnt=0;
//         for(int t:time){
//             cnt+=x/t;
//             if(cnt>=k)return 1;
//         }
//         return cnt>=k;
//     }
// };
//一眼丁真鉴定为忘开long long
posted @ 2025-12-08 09:55  射杀百头  阅读(68)  评论(0)    收藏  举报