Codeforces Round #703 (div2)

A. Shifting Stacks (*900)

题意

(n(1leq nleq 100))堆块,每堆有一个高度(h_i(0leq h_ileq 1e9)),可以从第(i)堆移动一个块到第((i+1))堆,能否实现整个序列严格单调递增?

题解

严格单调递增时,前(i)堆总和最小时,高度依次为(0,1,2,3,cdots),总和最小值(frac{i(i-1)}{2})。所以只需要检查每个位置的前缀和是否大于等于(frac{i(i-1)}{2})

#include<bits/stdc++.h>
#define LL long long
#define lowbit(x) x&(-x)
#define eps 1e-6
using namespace std;

const int maxn=110;

int T,n;
LL a[maxn];

int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
        LL need=0,sum=0;
        bool f=true;
        for(int i=1;i<=n;i++){
            need+=i-1;
            sum+=a[i];
            if(sum<need){
                f=false;
                break;
            }
        }
        printf("%s
",f?"YES":"NO");
    }
}

B. Eastern Exhibition (*1500)

题意

二维平面上有(n(1leq nleq 1000))座房屋,每座房屋有横纵坐标(x_i,y_i(0leq x_i,y_ileq 1e9)),计算平面上到所有房屋的曼哈顿距离最小的点的个数

题解

首先,问题不是二维的,因为两个一维互不影响,所以可以转化为对一维求解,再将答案相乘
对于一维情况,也就是一条直线上的点,有已知结论:总数为奇数时,中间的点符合条件,总数为偶数时,中间的两个点以及它们中间的点符合条件

#include<bits/stdc++.h>
#define LL long long
#define lowbit(x) x&(-x)
#define eps 1e-6
using namespace std;

const int maxn=1010;
int T,n;
LL x[maxn],y[maxn];

int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%lld %lld",&x[i],&y[i]);
        sort(x+1,x+1+n);
        sort(y+1,y+1+n);
        LL ans1=1,ans2=1;
        if(n%2==0){
            ans1=(x[n/2+1]-x[n/2])+1;
            ans2=(y[n/2+1]-y[n/2])+1;
        }
        printf("%lld
",ans1*ans2);
    }
}

C1. Guessing the Greatest (easy version) (*1600)

题意

交互题
有一个长度为(n(1leq n leq 1e5))的数组,每次可以选择一个区间([l,r](1leq l,r leq n)),询问区间内次大值的位置。在不超过(40)次询问中找到数组中最大值的下标

题解

二分
由于(2^{17}approx 1e5),所以使用二分时,需要在(2)次询问中确定最大值位于左半边还是右半边。首先询问([l,r])内的次大值的位置,设为(smax),之后二分区间([l,r]),设(mid=frac{(l+r)}{2}),在两个区间([l,mid])([mid+1,r])中,如果(smax)所在区间长度大于(1),再询问(smax)所在区间的次大值的位置,如果依然是(smax),则说明最大值在这个区间中,否则说明最大值在另一个区间中

#include<bits/stdc++.h>
#define LL long long
#define ULL unsigned long long
#define LD long double
#define PII pair<int,int>
#define PLL pair<LL,LL>
#define PLI pair<LL,int>
#define pi acos(-1.0)
#define eps 1e-6
#define lowbit(x) x&(-x)
using namespace std;

int n;

int main(){
    scanf("%d",&n);
    int l=1,r=n;
    while(r>l){
        printf("? %d %d
",l,r);
        fflush(stdout);
        int smax;
        scanf("%d",&smax);
        int mid=(l+r)>>1;
        if(smax<=mid){
            if(l==mid){
                l=mid+1;
                continue;
            }
            printf("? %d %d
",l,mid);
            fflush(stdout);
            int t;
            scanf("%d",&t);
            if(t==smax){
                r=mid;
            }
            else l=mid+1;
        }
        else{
            if(mid+1==r){
                r=mid;
                continue;
            }
            printf("? %d %d
",mid+1,r);
            fflush(stdout);
            int t;
            scanf("%d",&t);
            if(t==smax){
                l=mid+1;
            }
            else r=mid;
        }
    }
    printf("! %d
",r);
}

C2. Guessing the Greatest (hard version) (*1900)

题意

(c1)提高版,在不超过(20)次询问中找到数组中最大值的下标

题解

首先通过两次询问找到最大值位于次大值左边还是右边,具体方法是首先找到次大值的位置(smax),之后询问([1,smax]),如果结果仍为(smax),说明最大值位于([1,smax]),否则说明最大值位于([smax+1,r])。之后每次经过一次询问二分区间,询问的是以(smax)为一个端点,二分另一个端点得到的区间,总询问次数为(2+lceil log_21e5 ceil=19)

#include<bits/stdc++.h>
#define LL long long
#define ULL unsigned long long
#define LD long double
#define PII pair<int,int>
#define PLL pair<LL,LL>
#define PLI pair<LL,int>
#define pi acos(-1.0)
#define eps 1e-6
#define lowbit(x) x&(-x)
using namespace std;

int n;

int main(){
    scanf("%d",&n);
    printf("? %d %d
",1,n);
    fflush(stdout);
    int smax,t;
    scanf("%d",&smax);
    if(smax>1){
        printf("? %d %d
",1,smax);
        fflush(stdout);
        scanf("%d",&t);
    }
    int l,r;
    if(smax>1 && t==smax){
        l=1;
        r=smax-1;
        while(r>l){
            int mid=(l+r+1)>>1;
            printf("? %d %d
",mid,smax);
            fflush(stdout);
            int tt;
            scanf("%d",&tt);
            if(tt==smax) l=mid;
            else r=mid-1;
        }
    }
    else{
        l=smax+1;
        r=n;
        while(r>l){
            int mid=(l+r)>>1;
            printf("? %d %d
",smax,mid);
            fflush(stdout);
            int tt;
            scanf("%d",&tt);
            if(tt==smax) r=mid;
            else l=mid+1;
        }
    }
    printf("! %d
",r);
}

D. Max Median (*2100)

题意

有一个长度为(n(1leq nleq 2e5))的数组(a)(1leq a_ileq n),中位数定义为将一个序列按照递增排序之后下标为(lfloor frac{(n+1)}{2} floor)的数。给定(k(1leq kleq n)),计算数组(a)中长度至少为(k)并且中位数最大的连续子段的中位数的值

题解

二分答案,设二分出的值是(x),判断数组中是否存在满足条件,并且中位数大于等于(x)的子段
将数组(a)处理成只包含(-1,1)的数组,方法为如果(a_igeq x),则赋为(1),否则赋为(-1)。这样如果某个子段的中位数至少是(x),等价于子段和为正数
问题转化为寻找长度至少为(k),并且子段和最大的子段,判断子段和是否为正数
计算前缀和,扫一遍数组,计算以位置(i)结尾的满足条件的子段的最大子段和,具体方法是用(i)的前缀和减去(0,1,cdots,i-k)中最小的前缀和
时间复杂度(O(nlog n))

#include<bits/stdc++.h>
#define LL long long
#define lowbit(x) x&(-x)
#define eps 1e-6
using namespace std;

const int maxn=200010;

int n,k,a[maxn],b[maxn];

bool check(int x){
    for(int i=1;i<=n;i++){
        if(a[i]>=x) b[i]=1;
        else b[i]=-1;
    }
    for(int i=1;i<=n;i++) b[i]+=b[i-1];
    int ans=b[k];
    int mini=0;
    for(int i=k+1;i<=n;i++){
        mini=min(mini,b[i-k]);
        ans=max(ans,b[i]-mini);
    }
    return ans>0;
}

int main(){
    scanf("%d %d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    int l=1,r=n;
    while(r>l){
        int mid=(l+r+1)>>1;
        if(check(mid)) l=mid;
        else r=mid-1;
    }
    printf("%d
",r);
}

E

F

原文地址:https://www.cnblogs.com/fxq1304/p/14423936.html