Codeforces Round #655 (Div. 2) F.Omkar and Modes(交互 分治 二分)

题目

n(n<=2e5)个数的数组a[],ai在[1,1e9]间,数组内的数是非严格单增的,

现在需要猜出这个数组,

每次询问输入,? l r,代表询问[l,r]的众数,系统会返回这个区间内众数x及其次数f,

如存在多个众数,则会返回最小的那个,

保证数组内的不同元素数量为k(k<=min(n,25000)),

k未知,你需要不超过4*k次询问,询问出这个数组,最后输出不算次数

交互题日常,输出完之后fflush(stdout)

思路来源

https://blog.csdn.net/WAautomaton/article/details/107381374?utm_source=app

https://blog.csdn.net/m0_37795244/article/details/107300836?tdsourcetag=s_pctim_aiomsg

题解

代码二的做法不太会证次数<=4*k,于是主要搞一下代码1的做法,

设对[l,r]中询问x得到了次数f,并且已经知道了一个x的位置mid,

mid把f个数划分成了前后两半,f奇数则必有一半超过f/2个x,

f偶数则最坏情况两半均等于f/2个x,但后面那半会因为数小而留下

则[mid-f+1,f]这前f个数和[mid,mid+f-1]这后f个数中,必有一个区间,x是众数,

最坏情况,对两个区间都询问一下,

由于一个端点固定在mid,必能得到另一个端点,从而得到完整区间

所以先对[l,r]询问一次,然后如果能用一次得到一个x的位置,

再询问最多两次,就能找到x的位置[xl,xr],

然后分治[l,xl-1]和[xr+1,r]即可

 

考虑如何得到x的位置,实际上是均摊的每个数恰一次,

mp[x]=y代表已经得到了x的一个位置y,

先二分一下x,如果x已经在map里存在了直接返回

否则去

从v的位置后f个起,在[l,r]区间,往后f个f个找,这样由于x是f个的最小的,

在前面找的数必两两不同,为其贡献了新的一些数的位置,

且由于x出现了f次,间隔为f的找必不会错过x,

找到x的位置之后,执行前文提到的操作即可

代码1

#include
using namespace std;
const int N=2e5+10;
mapmp;
int n,a[N];
void ask(int l,int r,int &x,int &f){
    printf("? %d %d\n",l,r);
    fflush(stdout);
    scanf("%d%d",&x,&f);
}
void dfs(int l,int r){
    if(l>r){
        return;
    }
    int x,f;
    ask(l,r,x,f);
    if(r-l+1==f){
        mp[x]=l;
        for(int i=l;i<=r;++i){
            a[i]=x;
        }
        return;
    }
    auto p=mp.lower_bound(x);
    //找到x的mp的位置
    int xl,xr,nx,nf,mid;
    if(p!=mp.end() && p->first==x){
        mid=p->second;
    }
    else{
        p--;
        int a,b;
        for(int i=max(l,p->second+f);i<=r;i+=f){
            ask(i,i,a,b);
            mp[a]=i;
            if(a==x){
                mid=i;
                break;
            }
        }
    }
    ask(max(l,mid-f+1),mid,nx,nf);
    if(nx==x){
        xl=mid-nf+1;
        xr=xl+f-1;
    }
    else{
        ask(mid,min(r,mid+f-1),nx,nf);
        xr=mid+nf-1;
        xl=xr-f+1;
    }
    for(int i=xl;i<=xr;++i){
        a[i]=x;
    }
    dfs(l,xl-1);
    dfs(xr+1,r);
}
int main(){
    mp[0]=0;//二分前驱的最小
    scanf("%d",&n);
    dfs(1,n);
    printf("!");
    for(int i=1;i<=n;++i){
        printf(" %d",a[i]);
    }
    fflush(stdout);
    return 0;
}

代码2

#include
using namespace std;
const int N=2e5+10;
int n,a[N];
void ask(int l,int r,int &x,int &f){
    printf("? %d %d\n",l,r);
    fflush(stdout);
    scanf("%d%d",&x,&f);
}
void dfs(int l,int r){
    if(l>r){
        return;
    }
    int x,f;
    ask(l,r,x,f);
    int nr=r-f+1,nl=l+f-1;
    if(nr<=nl){
        for(int i=nr;i<=nl;++i){
            a[i]=x;
        }
        dfs(l,nr-1);
        dfs(nl+1,r);
    }
    else{
        int mid=(l+r)/2;
        dfs(l,mid);
        dfs(mid+1,r);
    }
}
int main(){
    scanf("%d",&n);
    dfs(1,n);
    printf("!");
    for(int i=1;i<=n;++i){
        printf(" %d",a[i]);
    }
    fflush(stdout);
    return 0;
}

 

你可能感兴趣的:(递归/分治,交互)