Codeforces Round #703 (Div. 2) C2. Guessing the Greatest (hard version)

这是一个关于解决交互式编程问题的博客,其中涉及到一个包含n个不同数字的数组。任务是在不超过20次查询内找出数组中最大元素的位置。通过询问子段的第二个最大值,逐步缩小范围,采用二分查找策略来确定最大值的索引。代码示例展示了如何实现这一过程,但作者提到实现起来有些复杂且不易调试。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

翻译:

简单版本和困难版本之间的唯一区别是查询数量的限制。

这是一个互动的问题。

有一个包含𝑛不同数字的数组𝑎。在一个查询中,您可以询问子段𝑎[𝑙..𝑟]中第二个最大元素的位置。在不超过20次查询中找到数组中最大元素的位置。

子段𝑎[𝑙..]𝑟]是所有的元素𝑎𝑙,𝑎𝑙+1,…,𝑎𝑟。在询问这个子段之后,你将得到这个子段在整个数组中的第二个最大值的位置。

输入
第一行包含一个整数𝑛(2≤𝑛≤105)-数组中的元素数量。

交互
您可以通过打印“?”𝑙𝑟”(1≤𝑙<𝑟≤𝑛)。答案是所有元素的第二个最大值的索引𝑎𝑙,𝑎𝑙+1,…,𝑎𝑟。数组𝑎是预先固定的,不能在交互时更改。

你可以通过打印“!”来输出答案。𝑝”,其中𝑝是数组中最大元素的索引。

您可以提出不超过20个查询。打印答案不算作查询。

打印查询后,不要忘记输出行尾并刷新输出。否则,您将得到闲置限制超过。要做到这一点,请使用:

c++中的fflush(stdout)或count .flush();
Java中的System.out.flush();
Pascal中的flush(输出);
Python中的stdout.flush();
有关其他语言,请参阅文档
黑客

要进行黑客攻击,请使用以下测试格式。

在第一行输出一个整数𝑛(2≤𝑛≤105)。在第二行中,输出𝑛整数1到𝑛的排列。𝑛在排列中的位置是最大值的位置

例子
inputCopy
5

3.

4

outputCopy
? 1 - 5

? 4个5

! 1
请注意
在示例中,假设𝑎为[5,1,4,2,3]。所以在问了[1..]5]子段4仅次于Max value,位置为3。问了[4..]5]子段2仅次于Max值,它在整个数组中的位置是4。

注意,还有其他数组𝑎会产生相同的交互,它们的答案可能不同。给出了示例输出,目的是为了理解交互作用。

思路:每次查询可以得到该位置第二小的坐标,我们第一次查询1~n,就可以得到全局第二小的值,之后我们查询1~第二小的坐标,或者第二小的坐标~n,便可以判断,最大值在哪个边界。

因为如果最大的在此区间内,返回值就会是第二小的坐标。然后我们可以根据左右区间,来不断二分查找,将第二小的坐标作为固定左端点或者固定右端点,然后不断二分即可。

感觉代码不太好写,本人快wa穿了,交互题,挺难调。

代码:

/*Looking! The blitz loop this planet to search way
 
 Only my RAILGUN can shoot it 今すぐ
 
 身体中を  光の速さで
 
 駆け巡った確かな予感
 
 掴め! 望むものなら残さず
 
 輝ける自分らしさで
 
 信じてるよ  あの日の誓いを
 
 この瞳に光る涙それさえも  強さになるから
 
 */
#include <iostream>
#include <algorithm>
#include <string.h>
#include <string>
#include <math.h>
#include <stdio.h>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<tuple>
#include<numeric>
using namespace::std;
typedef long long  ll;
inline __int128 read(){
    __int128 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;
}
inline void print(__int128 x){
    if(x < 0){
        putchar('-');
        x = -x;
    }
    if(x > 9)
        print(x / 10);
    putchar(x % 10 + '0');
}
int n;
int how(int x,int y){
    int ff;
    cout<<"? "<<x<<' '<<y<<endl;
    cin>>ff;return ff;
}
int w,mid;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(); cout.tie();
    cin>>n;
    if (n==1) {
        cout<<"! "<<1<<endl;return 0;
    }
    w=-1;
    int l=1,r=n,an;
    an=how(l,r);
//    if (n==2) {
//        cout<<"! "<<3-an<<endl;return 0;
//    }
    if (an!=1) {
        w=how(1, an);
    }
//    printf("%d %d\n",w,an);
    if (w==an) {
        r=an-1;
        while (l<r) {
        mid=(l+r+1)>>1;
        w=how(mid, an);
        if (w==an)
            l=mid;
        else
            r=mid-1;
        }
    }
    else{
        l=an+1;
        while (l<r) {
            mid=(l+r)>>1;
            w=how(an, mid);
            if (w==an)
                r=mid;
            else
                l=mid+1;
            }
        }
    cout<<"! "<<l<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值