Loading

gym102341C Cloyster (Radewoosh+mnbvmar Contest (supported by AIM Tech)) 二分

题意

\(n\times n\ (n\le2000)\)的格子,每个格子有一个数字,数字各不相同。除了最大的数字每个数字周围八个格子必有比他大的格子。你可以进行\(3*n+210\)次询问,每次可以询问某个位置的值。求最大格子大小。

分析

二分减小范围题。考虑\(n\times n\)的情况,取中间一行的最大值,记为\(A_1\)。再观察它的周围6个格子,如果上方存在比\(A_1\)大的格子\(A_2\),由于每个点一直向周围比他大的点走一定可以走到最大值,而\(A_2\)显然不能穿过中间这行,所以最大值一定再上半部分。下方同理。若不存在,则\(A_1\)为最大值。

特别的,当横向划分后进行竖向划分时,可能存在一条路径穿过横线到了竖线的另一边。这时,我们可以保存横向划分时的最大值位置\(A_2\),若竖线两边不存在大于\(A2\)的值,则可以确定最大值与\(A_2\)在竖线同一边。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e3+5;

int tx[]={-1,-1,-1,0,0,1,1,1};
int ty[]={-1,0,1,-1,1,-1,0,1};

int n,lx,rx,ly,ry;
int maxx,maxi,maxj;
int vis[maxn][maxn];

bool in(int x,int y){
    return x>=1 && x<=n && y>=1 && y<=n;
}

int get(int x,int y){
    if(vis[x][y])return vis[x][y];

    cout<<"? "<<x<<' '<<y<<endl;
    cin>>vis[x][y];
    if(vis[x][y]>maxx){
        maxx=vis[x][y];
        maxi=x;
        maxj=y;
    }
    return vis[x][y];
}

void cuti(int i,int j1,int j2)
{
    int maxx=0,ii,jj;
    for(int j=j1;j<=j2;j++)
    {
        int tmp=get(i,j);
        if(tmp>maxx){
            maxx=tmp;
            ii=i;
            jj=j;
        }
    }
    for(int j=max(j1,jj-1);j<=min(j2,jj+1);j++)
    {
        if(i-1>=lx)
            get(i-1,j);
        if(i+1<=rx)
            get(i+1,j);
    }

    if(maxi<i)
        rx=i-1;
    else if(maxi>i)
        lx=i+1;
    else
    {
        cout<<"! "<<ii<<' '<<jj<<endl;
        exit(0);
    }
}

void cutj(int i1,int i2,int j)
{
    int maxx=0,ii,jj;
    for(int i=i1;i<=i2;i++)
    {
        int tmp=get(i,j);
        if(tmp>maxx){
            maxx=tmp;
            ii=i;
            jj=j;
        }
    }
    for(int i=max(i1,ii-1);i<=min(i2,ii+1);i++)
    {
        if(j-1>=ly)
            get(i,j-1);
        if(j+1<=ry)
            get(i,j+1);
    }
    if(maxj<j)
        ry=j-1;
    else if(maxj>j)
        ly=j+1;
    else
    {
        cout<<"! "<<ii<<' '<<jj<<endl;
        exit(0);
    }
    

}

int main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    lx=ly=1;
    rx=ry=n;
    while(lx<rx || ly<ry)
    {
        if(lx<rx){
            cuti((lx+rx)/2,ly,ry);
        }
        if(ly<ry){
            cutj(lx,rx,(ly+ry)/2);
        }
    }
    cout<<"! "<<lx<<' '<<ly<<endl;

    return 0;
}
posted @ 2020-12-10 11:13  intmian  阅读(234)  评论(0编辑  收藏  举报