交互题合集

简介

交互题是一种有趣的题目类型,题目一般会有一个目标值,如数组和,数组中的最大值等,为了求出这个目标值,允许进行限制次数的指定类型的询问,如询问 \(a_i+a_j\) 的值,系统会对每个询问给出结果,这就是交互的过程。在所有询问执行完后需要求出目标值并输出

题目

CF679A.Bear and Prime 100

题目链接CF679A

题意:有一个在区间 \([2,100]\) 内的未知整数 \(x\) ,你需要判断它是否为素数。可以进行不超过 \(20\) 次询问,每次可以询问一个由你指定的整数 \(d\) 是否为 \(x\) 的约数

分析:当 \(x\) 为合数时,可以表示为 \(x=nm\)\(n\geq 2\) ,所以 \(m\leq 50\) ,所以根据算术分解定理, \(x\) 一定会被小于等于 \(50\) 的素因子整除,枚举可知这样的素因子一共有 \(15\) 个。那么我们进行 \(15\) 次询问,计算 \(x\) 可以被多少个素因子整除,如果被两个或以上的素因子整除,那么 \(x\) 一定是合数,如果只被一个素因子 \(p\) 整除,那么需要判断 \(x=p^q\) 的情况,所以再进行一次询问,判断 \(p^2\) 是否整除 \(x\) ,若整除则 \(x\) 为合数,否则为素数

#include<bits/stdc++.h>
using namespace std;

int p[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47};

bool query(int x)
{
    cout << x << endl;
    string s;
    cin >> s;
    return s == "yes";
}

int main()
{
    int sum = 0, div;
    for(int i = 0; i < 15; i++) {
        if(query(p[i])) {
            sum++;
            div = p[i];
        }
    }
    if(sum >= 2) {
        cout << "composite" << endl;
    } else {
        if(div > 10)
            cout << "prime" << endl;
        else
            cout << (query(div * div) ? "composite" : "prime") << endl;
    }
    return 0;
}

CF727C.Guess the Array

题目链接CF727C

题意:有一个长度为 \(n\) 的未知整数数组,需要求出每个元素的值。可以进行不超过 \(n\) 次询问,每次可以询问 \(a_i+a_j\) 的值

分析:考虑 \(n=3\) 时如何求解。容易想到询问三次,分别记 \(s_1=a_1+a_2,s_2=a_1+a_3,s_3=a_2+a_3\) 那么 \(2a_1=(s_1+s_2-s_3)\) ,求出 \(a_1\) 后再 \(a_2=s_1-a_1,a_3=s_2-a_1\) 即可。那么当 \(n\) 大于 \(3\) 时要求 \(a_i\) 的值只需询问 \(a_1+a_i\) ,而 \(a_1\) 已知,这样就解决了问题

#include<bits/stdc++.h>
using namespace std;

int ans[5000 + 5];

int query(int x, int y)
{
    cout << "? " << x << ' ' << y << endl;
    int res;
    cin >> res;
    return res;
}

int main()
{
    int n;
    cin >> n;
    int s1 = query(1, 2), s2 = query(1, 3), s3 = query(2, 3);
    ans[1] = (s1 + s2 - s3) / 2;
    ans[2] = s1 - ans[1];
    ans[3] = s3 - ans[2];
    for(int i = 4; i <= n; i++)
        ans[i] = query(1, i) - ans[1];
    cout << '!';
    for(int i = 1; i <= n; i++)
        cout << ' ' << ans[i];
    cout << endl;
    return 0;
}

CF1556D.Take a Guess

题目链接CF1556D

题意:有一个长度为 \(n\) 的未知整数数组,需要求出每个元素的值。可以进行不超过 \(2n\) 次询问,每次可以询问 \(a_i|a_j\)\(a_i\&a_j\) 的值

分析:可以发现一个恒等式 \(a|b+a\&b=a+b\) ,那么用 \(2\) 次询问就可以求得 \(a_i+b_i\) ,所以就可以转化为上一个问题

#include<bits/stdc++.h>
using namespace std;

int n, k;
int a[10000 + 5];

int query(int x, int y)
{
    int res1, res2;
    cout << "or " << x << ' ' << y << endl;
    cin >> res1;
    cout << "and " << x << ' ' << y << endl;
    cin >> res2;
    return res1 + res2;
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> n >> k;
    int s1 = query(1, 2), s2 = query(1, 3), s3 = query(2, 3);
    a[1] = (s1 + s2 - s3) / 2;
    a[2] = s1 - a[1];
    a[3] = s2 - a[1];
    for(int i = 4; i <= n; i++)
        a[i] = query(1, i) - a[1];
    sort(a + 1, a + n + 1);
    cout << "finish " << a[k] << endl;
    return 0;
}

CF1451E1.Bitwise Queries

题目链接CF1451E1

题意:有一个长度为 \(n\) 的未知整数数组,需要求出每个元素的值。可以进行不超过 \(n+2\) 次询问,每次可以询问 \(a_i|a_j\)\(a_i\&a_j\)\(a_i\oplus a_j\) 的值

分析:可以发现这道题是上一道题的加强版,多了一个异或的询问种类。考虑如何用异或降低询问次数,可以先用上一道题的方法求出 \(a_1,a_2,a_3\) 的值,对于每个 \(i>3\) ,询问 \(a_i\oplus a_1\) ,而 \(a_i=a_i\oplus a_1\oplus a_1\) ,所以只需一次询问即可求出。这样一共用了 \(n-3+6=n+3\) 次询问,仍不满足要求。考虑另一个恒等式 \(a\oplus b+2a\&b=a+b\) ,那么我们只要询问 \(a_1\oplus a_2\)\(a_1\oplus a_3\) 就可以得到 \(a_2\oplus a_3\) ,此时只用了 \(2\) 次询问,再用 \(3\) 次询问 \(a_1\&a_2,a_1\&a_3,a_2\&a_3\) ,总共进行了 \(n-3+5=n+2\) 次询问,满足要求

#include<bits/stdc++.h>
using namespace std;

int n;
int a[65540];

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> n;
    int res1, res2, res3, res4, res5;
    cout << "AND 1 2" << endl;
    cin >> res1;
    cout << "AND 1 3" << endl; 
    cin >> res2;
    cout << "XOR 1 2" << endl;
    cin >> res3;
    cout << "XOR 1 3" << endl; 
    cin >> res4;
    cout << "AND 2 3" << endl;
    cin >> res5;
    a[1] = (res3 + 2 * res1 + res4 + 2 * res2 - 2 * res5 - (res3 ^ res4)) / 2;
    a[2] = 2 * res1 + res3 - a[1];
    a[3] = 2 * res2 + res4 - a[1];
    for(int i = 4; i <= n; i++) {
        int t;
        cout << "XOR 1 " << i << endl;
        cin >> t;
        a[i] = t ^ a[1];
    }
    cout << '!';
    for(int i = 1; i <= n; i++)
        cout << ' ' << a[i];
    cout << endl;
    return 0;
}

CF1207E.XOR Guessing

题目链接CF1207E

题意:有一个在区间 \([0,2^{14}-1]\) 内的未知整数 \(x\) ,你可以进行 \(2\) 次询问,每次询问要给出 \(100\) 个在区间 \([0,2^{14}-1]\) 内不同的数字,系统会在这些数中随机挑选一个并回答它与 \(x\) 的异或值,需要求出 \(x\) 的值

分析:由于系统会在给定的数中随机挑选一个回答,那么必须保证给出的任意一个数被挑选时都能得到有用的信息。由于 \(0\) 与任何数的异或值都为那个数,所以考虑在二进制表示下把前 \(7\) 位设为 \(0\) ,那么询问一定可以得到 \(x\)\(7\) 位的值,那么第二次询问很显然是把后 \(7\) 位设为 \(0\) ,将两次询问的结果合并即可得到 \(x\)

#include<bits/stdc++.h>
using namespace std;

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int res1, res2;
    cout << '?';
    for(int i = 1; i <= 100; i++)
        cout << ' ' << i;
    cout << endl;
    cin >> res1;
    cout << '?';
    for(int i = 1; i <= 100; i++)
        cout << ' ' << i * 128;
    cout << endl;
    cin >> res2;
    cout << "! " << res1 / 128 * 128 + res2 % 128 << endl;
    return 0;
}
posted @ 2022-03-24 08:56  f(k(t))  阅读(441)  评论(0编辑  收藏  举报