Codeforces Round #703 (Div. 2)

B

思路:结论题,如果点是奇数,这个点只能为1

否则等于中间最短点之间的差值+1

#include<stdio.h>
#include<math.h>
#include<string.h>
#include<ctype.h>
#include<iostream>
#include<algorithm>
#include<vector> 
typedef long long ll;
using namespace std;
inline void solve(){
    int len;cin>>len;
    vector<ll> a(len+3),b(len+3);
    for(int i=0;i<len;i++) cin>>a[i]>>b[i];
    sort(a.begin(),a.begin()+len);
    sort(b.begin(),b.begin()+len);
    if(len%2==1){
        cout<<"1\n";return;
    }
    int l=(len-1)/2,r=len/2;
    cout<<(a[r]-a[l]+1)*(b[r]-b[l]+1)<<endl;
}
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0),std::cout.tie(0);
    int sum;cin>>sum;
    while(sum--){
        solve();
    }
}

C:

思路:思路是二分加构造,首先讨论在整个范围内x的次大值,然后再查询在1到x这个范围内次小值是否成立,那么最大值在左边界,不是最大值就在右边界

 

#include<stdio.h>
#include<math.h>
#include<string.h>
#include<ctype.h>
#include<iostream>
#include<algorithm>
#include<vector> 
typedef long long ll;
using namespace std;
int check(int l,int r){
    cout<<"? "<<l<<' '<<r<<"\n";
    cout.flush();
    int x;cin>>x;
    return x;
}
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0),std::cout.tie(0);
    int n,ans;cin>>n;
    int x=check(1,n);//标记次大值
    if(x==1||check(1,x)!=x){//答案在x右边
        int l=x+1,r=n;
        while(l<=r){
            int mid=(l+r)>>1,pp=check(x,mid);
            if(pp==x)ans=mid,r=mid-1;//说明在x+1到mid这个范围内,存在最大值,然后让边界往左边收缩(因为x在左是已知的
            else l=mid+1;
        }
    }
    else{
        int l=1,r=x-1;
        while(l<=r){
            int mid=(l+r)>>1,pp=check(mid,x);//同理,由于x已经定位在右侧,所以边界往右边收缩
            if(pp==x)ans=mid,r=mid-1;
            else l=mid+1;
        }
    }
    cout<<"! "<<ans<<"\n";
}

 另一种写法

#include<stdio.h>
#include<math.h>
#include<string.h>
#include<ctype.h>
#include<iostream>
#include<algorithm>
#include<vector> 
typedef long long ll;
using namespace std;
int check(int l,int r){
    cout<<"? "<<l<<' '<<r<<"\n";
    cout.flush();
    int x;cin>>x;
    return x;
}
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0),std::cout.tie(0);
    int n,ans;cin>>n;
    int x=check(1,n);
    if(x!=1&&check(1,x)==x){//在x左方向
        int l=1,r=x-1;
        while(l!=r){
            int mid=l+r+1>>1;
            if(check(mid,x)==x)l=mid;//mid逐渐向x靠拢
            else r=mid-1;
        }
        cout<<"! "<<l<<"\n";
    }
    else{
        int l=x+1,r=n;
        while(l!=r){
            int mid=l+r>>1;
            if(check(x,mid)==x)r=mid;
            else l=mid+1;
        }
        cout<<"! "<<l<<"\n";
    }
}

(尽量画个图判断一下,补题的时候左右不分浪费了好多时间)

 D:

这里有一个常用的思维trick:假设某个数x为一段连续子序列的中位数,用一个额外的数组存储每位值,<x的为-1,大于等于的为1,最后把这些数都加起来,大于等于0说明这段序列的中位数大于等于x

先上代码

#include<stdio.h>
#include<math.h>
#include<string.h>
#include<ctype.h>
#include<iostream>
#include<algorithm>
#include<vector> 
typedef long long ll;
using namespace std;
int len,k;
int num[200009],pre[200009],prmin[200009];
bool check(int x){
    for(int i=0;i<200009;i++) pre[i]=0,prmin[i]=0;
    for(int i=1;i<=len;i++){
        pre[i]=pre[i-1];prmin[i]=prmin[i-1];//统计pre状态
        if(num[i]>=x)pre[i]++;
        else pre[i]--;
        if(pre[i]<prmin[i]) prmin[i]=pre[i];//统计在i状态下前面所有pre的最小值
    }
    for(int i=k;i<=len;i++)//数组长度必然从K开始
        if(pre[i]-prmin[i-k]>0)//减少最少的次数
            return 1;
    return 0;
}
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0),std::cout.tie(0);
    cin>>len>>k;
    for(int i=1;i<=len;i++) cin>>num[i];
    int l=1,r=len,ans;
    while(l<=r){
        int mid=l+r>>1;
        if(check(mid)) ans=mid,l=mid+1;
        else r=mid-1;
    }
    cout<<ans<<"\n";
}

看完那些题解之后我个人是比较疑惑单调性为什么存在

感谢这位大佬的题解(32条消息) CF1486D - Max Median-巧妙二分答案_塔子哥来了的博客-CSDN博客

首先题目有一个关键信息

 

 联系我上面所说的结论,我们可以去二分中位数数值,

设最终二分的答案为x,<=x的数值check都成立,大于x的check都不成立,即证明单调性

posted on 2022-05-30 20:22  zesure  阅读(30)  评论(0编辑  收藏  举报

导航