Find an element in hidden array CodeChef - ELHIDARR (交互题+两次二分)

Find an element in hidden array CodeChef - ELHIDARR (交互题+两次二分)

There is an array of length N consisting of non-negative integers. The array is sorted in non-decreasing order. Each number in the array appears exactly K times, except one element, which appears at least once, but less than K times. Your task is to identify that element.

This is an interactive problem. You are only given the integer N in the input. Both the array and the value of K are hidden. You are allowed to ask the judge the following queries: What is the value of the element at index i of the array? Identify the value of the element with frequency less than K by asking at most 60 such queries.

Input and Output
The first line of the input contains a single integer T denoting the number of test cases.

For each test case, you should start by reading a single line containing one integer N from the input.

You can interact with the judge using the standard input and output. There are two types of operations: to perform one operation, you should print to the standard output a line containing two space-separated integers type and val.

If type = 1, you are asking the judge a query for the value of the element of the array at index val. After printing this line, the judge will print to the standard input a line containing one integer corresponding to the value of the element at index val.
If type = 2, you are telling the judge that the element with frequency less than K is val. For each test case, you should perform this operation exactly once at the end. This is not counted towards the 60 queries.
Note
Don’t forget to flush the standard output after printing each line. It can be done using fflush(stdout) in C/C++, System.out.flush() in Java and sys.out.flush() in Python.

If you ask more than 60 queries, your program will get the verdict Wrong Answer.

Constraints
1 ≤ T ≤ 1e4
3 ≤ N ≤ 1e5
2 ≤ K ≤ N - 1
each element of the array lies between 1 and 1e9 inclusive
Example
Input / judge feedback your output
1
3
1 2
1
1 3
5
1 1
1
2 5
Explanation
Example case 1: Suppose the array is [1, 1, 5]. Note that the value of K is 2, but it is hidden from you.

In the first query, you request the value of the 2nd element and the judge answers 1. Then you request the value of the 3rd element and the judge answers 5, then the value of the first element and the judge answers 1.

Now, you tell the judge that the answer is 5. You made a total of 3 queries.

题意

T组数据
有一个长度为N的非严格递增的数组,所有的数值出现次数都为K(只有一个特殊的数值出现次数为【1,k-1】),你需要在60次询问操作内找出它。
你可以每次询问在位置pos上的数的值的大小

输入T、N
对于询问操作得先“输入”1,之后“输入”想查询值的位置(即下标)
最后输出答案前先输出一个2,之后再输出答案
另外这题是交互题,输入输出要注意

//询问位置为3上的值,结果会返回到读取中,即a[3]中
printf("1 3\n");
fflush(stdout);
scanf("%d",&a[3]);

T范围【1,1e4】
N范围【3,1e5】
K范围【2,N-1】

思路

两次二分。第一次二分找出K,第二次二分找出特殊的值。
因为所有数值是非严格递增排列
如1 1 1 3 3 3 4 4 4 5 5 6 6 6 8 8 8
很容易发现,上面的数据中K=3,特殊的数为5,它出现的次数为2
然后发现这样的一件事,在特殊的数之前的数的下边符合下面的规律

a[1] = a[k]
a[k+1]=a[k+k]
a[2k+1]=a[3k]
。。。

a[1]!=a[k+1]
a[k+1]!=a[2k+1]
。。。
而对于出现在特殊数后面的
a[num
k+1]!=a[num*k+k] (num为正整数)

有了上面的发现后就可以二分区间特殊的数出现的位置了

代码如下

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int maxn=1e5+10;
int a[maxn];
bool vis[maxn];
int check(int n)  //二分求K,注意答案为返回的left-1或者right
{
    int left=1,right=n;
    int mid;
    printf("1 1\n");
    fflush(stdout);
    scanf("%d",&a[1]);
    vis[1]=true;
    while(left<=right){
        mid=(left+right)/2;
        if(vis[mid]==false){
            printf("1 %d\n",mid);
            fflush(stdout);
            scanf("%d",&a[mid]);
            vis[mid]=true;
        }
        if(a[1]==a[mid]){
            left=mid+1;
        }
        else right=mid-1;
    }
    return left;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        memset(vis,false,sizeof(vis));
        //memset(a,0,sizeof(a));
        int n;
        scanf("%d",&n);
        int k=check(n)-1;
        if(vis[n]==0){
            printf("1 %d\n", n);
            fflush(stdout);
            scanf("%d",&a[n]);
            vis[n]=true;
        }
        if(2*k+1<=n){
            if(vis[k+1]==false){
                printf("1 %d\n", k+1);
                fflush(stdout);
                scanf("%d",&a[k+1]);
                vis[k+1]=true;
            }
            if(vis[k+k+1]==false){
                printf("1 %d\n", k+k+1);
                fflush(stdout);
                scanf("%d",&a[k+k+1]);
                vis[k+k+1]=true;
            }
            if(a[k+1]==a[k+k+1]){  //说明第一个数就是特殊的数
                printf("2 %d\n", a[1]);
                fflush(stdout);
                continue;
            }
            else{
                int left=1;
                int right=n/k;  //分块
                while(left<=right){  //二分锁定特殊的数出现的位置
                    int mid=(left+right)/2;
                    int aa=mid*k;  
                    int bb=aa+1;  
                    if(vis[aa]==false){
                        printf("1 %d\n", aa);
                        fflush(stdout);
                        scanf("%d",&a[aa]);
                        vis[aa]=true;
                    }
                    if(vis[bb]==false){
                        printf("1 %d\n", bb);
                        fflush(stdout);
                        scanf("%d",&a[bb]);
                        vis[bb]=true;
                    }
                    if(a[aa]!=a[bb]){ //说明特殊的数在后面
                        left=mid+1;
                    }
                    else{
                        right=mid-1;
                    }
                }
                left--;
                int ans;
                if(vis[left*k+1]==false){
                    printf("1 %d\n", left*k+1);
                    fflush(stdout);
                    scanf("%d",&a[left*k+1]);
                    vis[left*k+1]=true;
                    ans=a[left*k+1];
                }
                else ans=a[left*k+1];
                printf("2 %d\n", ans);
                fflush(stdout);
            }
        }
        else{  //说明只有两种数,且第一个数值出现的次数多,说明特殊的数肯定是第二种
            printf("2 %d\n", a[n]);
            fflush(stdout);
        }
    }
    return 0;
}


你可能感兴趣的:(二分查找,交互题)