【Codeforces Round 838 div2 A ~ D】

Codeforces Round 838 div2

记录一下读假了的 C


A. Divide and Conquer

Problem Summary

这题题意是,如果一个数组的所有元素和是偶数,就称它是一个好数组。现给定一个数组 a a a,支持一个操作:选中 a i a_i ai 并将它变为 ⌊ a i 2 ⌋ \lfloor \frac{a_i}{2} \rfloor 2ai(下取整),问最少需要多少步才能将整个数组变成好数组。

Solution

由于数组和为偶数是好数组,所以如果原来的数组和是偶数,或者说数组中的奇数数量为偶数,那就是好数组,即需要 0 0 0 步修改。若奇数数量为奇数,那就说明需要修改,而修改就只有两种方向,一种是将某个 a i a_i ai 由奇变偶,一种是将某个 a i a_i ai 由偶变奇,这样就能使得奇数数量变为偶数。所以我们只需要从前往后扫一遍,对于奇数,我们需要判断它第一次变成偶数需要除多少次 2 2 2,对于偶数,我们需要判断它第一次变成奇数需要除多少次 2 2 2,对每个数都取一个最小值。

Code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
#include <cmath>
#include <unordered_set>
#include <set>
#include <unordered_map>
#include <map>
#include <stack>
#include <assert.h>

#define endl '\n'

#define x first
#define y second

#define ls u << 1
#define rs u << 1 | 1

#define l(x) tr[x].s[0]
#define r(x) tr[x].s[1]

#define pb push_back
#define ppb pop_back()
#define all(x) x.begin(), x.end()
#define debug(x) cout << (#x) << ' ' << x << endl

#define fup(i, a, b) for (int i = a; i <= b; i ++ )
#define fdn(i, a, b) for (int i = a; i >= b; i -- )

using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
typedef double db;
typedef long double LD;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
typedef pair<double, double> PDD;
typedef pair<string, int> PSI;
typedef pair<char, int> PCI;

const int N = 100010, M = N << 1, MOD = 1e9 + 7, INF = 0x3f3f3f3f;
const db eps = 1e-8;
const db PI = acos(-1);

const int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};

int n, m;
int a[N];

void solve()
{
    cin >> n;
    fup(i, 1, n) cin >> a[i];
    int cnt = 0;
    fup(i, 1, n) if (a[i] & 1) cnt ++ ;
    if (cnt % 2 == 0)
    {
        cout << 0 << endl;
        return;
    }
    int res = INF;
    for (int i = 1; i <= n; i ++ )
        if (a[i] & 1)
        {
            int x = a[i], s = 0;
            while (x & 1) x >>= 1, s ++ ;
            res = min(res, s);
        }
        else
        {
            int x = a[i], s = 0;
            while (x % 2 == 0) x >>= 1, s ++ ;
            res = min(res, s);
        }
    cout << res << endl;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int T = 1;
    cin >> T;
    while (T -- ) solve();

    return 0;
}

B. Make Array Good

Problem Summary

这题是说对于一个数组 b b b,如果任取两个数 b i ,   b j b_i, \space b_j bi, bj 1 ⩽ i ,   j ⩽ n 1 \leqslant i, \space j \leqslant n 1i, jn),都满足其中较小的能够整除较大的,即 m i n ( b i , b j ) ∣ m a x ( b i , b j ) min(b_i, b_j) | max(b_i, b_j) min(bi,bj)max(bi,bj),就称 b b b 为一个好数组。现给定一个数组 a a a,长度为 n n n 1 ⩽ n ⩽ 1 0 5 1 \leqslant n \leqslant 10^5 1n105 1 ⩽ a i ⩽ 1 0 9 1 \leqslant a_i \leqslant 10^9 1ai109。定义一次操作:选定 a i a_i ai,并加上一个 x x x 0 ⩽ x ⩽ a i 0 \leqslant x \leqslant a_i 0xai),同时要满足修改完的 a i ⩽ 1 0 18 a_i \leqslant 10^{18} ai1018 。你需要进行至多 n n n 次操作,将 a a a 变为好数组,输出一种修改方案即可,不需要最小化操作次数。

Solution

首先一个数一定能写出二进制表示,并且这题的 a i ⩾ 1 a_i \geqslant 1 ai1,这就意味着它一定会落在 [ 2 k , 2 k + 1 ) [2^k, 2^{k + 1}) [2k,2k+1) 内, ∀ k ∈ N \forall k \in N kN,因此,将这个区间内的一个数变为 2 2 2 的整次幂,所加的 x x x 一定不会超过自身。对于恰好等于 2 k 2^k 2k 的数来说,它就不需要动了;对于落在 ( 2 k , 2 k + 1 ) (2^k, 2^{k + 1}) (2k,2k+1) 开区间内的数,一定可以一次将其变成 2 k + 1 2^{k + 1} 2k+1,且至多花费 2 k − 1 2^k - 1 2k1,不会超过自身。同时,由于 2 30 = 1073741824 2^{30} = 1073741824 230=1073741824,而 a i a_i ai 最大是 1 0 9 10^9 109,所以修改后 a i a_i ai 的上限就是 2 30 2^{30} 230,所以也满足修改后 a i ⩽ 1 0 18 a_i \leqslant 10^{18} ai1018 的限制。

综上,我们只要找到对每个数来说第一个大于等于它的 2 2 2 的整次幂。而为了书写方便,我们将不需要修改的数也看做要修改,即加的数 x x x 视为 0 0 0,这样就可以有一个总共修改 n n n 次的方案。

Code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
#include <cmath>
#include <unordered_set>
#include <set>
#include <unordered_map>
#include <map>
#include <stack>
#include <assert.h>

#define endl '\n'

#define x first
#define y second

#define ls u << 1
#define rs u << 1 | 1

#define l(x) tr[x].s[0]
#define r(x) tr[x].s[1]

#define pb push_back
#define ppb pop_back()
#define all(x) x.begin(), x.end()
#define debug(x) cout << (#x) << ' ' << x << endl

#define fup(i, a, b) for (int i = a; i <= b; i ++ )
#define fdn(i, a, b) for (int i = a; i >= b; i -- )

using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
typedef double db;
typedef long double LD;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
typedef pair<double, double> PDD;
typedef pair<string, int> PSI;
typedef pair<char, int> PCI;

const int N = 100010, M = N << 1, MOD = 1e9 + 7, INF = 0x3f3f3f3f;
const db eps = 1e-8;
const db PI = acos(-1);

const int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};

int n, m;
int a[N];
LL b[N];

LL qmi(LL a, LL b)
{
    LL res = 1;
    while (b)
    {
        if (b & 1) res *= a;
        a *= a;
        b >>= 1;
    }
    return res;
}

void solve()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ ) cin >> a[i];
    int res = 0;
    fup(i, 1, n)
    {
        LL l = 0, r = 32;
        while (l < r)
        {
            LL mid = l + r >> 1;
            if (qmi(2, mid) >= a[i]) r = mid;
            else l = mid + 1;
        }
        b[i] = qmi(2, r) - a[i];
    }
    cout << n << endl;
    fup(i, 1, n) cout << i << ' ' << b[i] << endl;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int T = 1;
    cin >> T;
    while (T -- ) solve();

    return 0;
}

C. Binary Strings are Fun

Problem Summary

C C C 我是真的看了好久好久好久,写到最后发现还是读假了,这个 m e d i a n median median 害人不浅(悲),但还好最后思考出来了。

它的意思是这样的。首先题目给的字符串全都是 01 01 01 串,接着是 m e d i a n median median 的概念:有一个 长度为 2 m − 1 2m - 1 2m1 的字符串 a a a,在这个串中, a a a m e d i a n median median 定义为在 a a a 中出现次数至少为 m m m 次的元素。换成人话就是,在一个长度为奇数的串中,如果 0 0 0 出现次数超过了这个串长度的一半(这里的一半是下取整),那 0 0 0 就是这个串的 m e d i a n median median 1 1 1 也同理。

定义 好字符串:一个长度为 m m m m m m 是奇数)的字符串 b b b,如果满足所有奇数下标上的数是当前前缀的 m e d i a n median median,那就说这个串是一个好串,用比较严谨的描述就是, 1 ⩽ i ⩽ m 1 \leqslant i \leqslant m 1im,且 i i i 是奇数,其对应的数为 b i b_i bi b i b_i bi 就是 b b b 的前缀 b [ 1... i ] b[1...i] b[1...i] m e d i a n median median

定义 好扩展:这里我就不翻译原话了,直接说人话,它的意思就是 —— 对一个长度是 k k k 的字符串 a a a,它的中间有 k − 1 k - 1 k1 个空隙,现在要往这 k − 1 k - 1 k1 个空隙里插入 0 0 0 1 1 1,设插入后的字符串为 b b b,若 b b b 是一个好字符串,那么就称这是一个好扩展。

定义 f ( s ) f(s) f(s):表示字符串 s s s 的好扩展个数。

现给定字符串 a a a,长度为 n n n 1 ⩽ n ⩽ 2 × 1 0 5 1 \leqslant n \leqslant 2 \times 10^5 1n2×105,求 ∑ i = 1 n f ( a [ 1... i ] ) \sum\limits_{i = 1}^n{f(a[1...i])} i=1nf(a[1...i]),即求 a a a 所有前缀的好扩展和。

把这个定义读通了之后,其实难度就不算大了,这题的题意我个人感觉实在太绕了。

Solution

我们将长度为 i i i 的前缀分为两大类,一种是 a i a_i ai a i − 1 a_{i - 1} ai1 相等,一种是不等。

  • C a s e   1 Case \space 1 Case 1 a i ≠ a i − 1 a_i \not = a_{i - 1} ai=ai1

  • 不妨设 a i = 1 ,   a i − 1 = 0 a_i = 1, \space a_{i - 1} = 0 ai=1, ai1=0,这里反过来是完全对称的情况。此时长度为 i i i 前缀需要扩展出一个好字符串,那也就意味着需要在满足长度为 i − 1 i - 1 i1 的前缀的好扩展的基础上继续扩展,因此我们列一列长度为 i − 1 i - 1 i1 的好扩展需要满足的不等式。由于此时有 a i − 1 = 0 a_{i - 1} = 0 ai1=0,所以满足如下式子: c n t [ 0 ] ⩾ i − 1 cnt[0] \geqslant i - 1 cnt[0]i1 c n t [ 1 ] ⩽ i − 2 cnt[1] \leqslant i - 2 cnt[1]i2 c n t [ 0 ] + c n t [ 1 ] = 2 × ( i − 1 ) − 1 cnt[0] + cnt[1] = 2 \times (i - 1) - 1 cnt[0]+cnt[1]=2×(i1)1 而当前长度为 i i i 的前缀在满足这些的基础上,还要满足对当前 a i a_i ai 的式子: c n t ′ [ 1 ] ⩾ i cnt'[1] \geqslant i cnt[1]i c n t ′ [ 0 ] ⩽ i − 1 cnt'[0] \leqslant i - 1 cnt[0]i1 c n t ′ [ 0 ] + c n t ′ [ 1 ] = 2 × i − 1 cnt'[0] + cnt'[1] = 2 \times i - 1 cnt[0]+cnt[1]=2×i1 当前长度为 i i i 的前缀的好扩展,是在长度为 i − 1 i - 1 i1 的前缀的基础上再加一个 a i a_i ai a i − 1 a_{i - 1} ai1 a i a_i ai 之间的插入数字所组成的,而在只加入 a i a_i ai 时,有 c n t ′ [ 1 ] = c n t [ 1 ] + 1 ⩽ i − 1 cnt'[1] = cnt[1] + 1 \leqslant i - 1 cnt[1]=cnt[1]+1i1 若是如此,就不能满足 c n t ′ [ 1 ] ⩾ i cnt'[1] \geqslant i cnt[1]i 的要求,至少还需要加一个 1 1 1,同时,由于此时只能再插入一个数字,所以至多只能再加入一个 1 1 1,因此必须有 c n t ′ [ 1 ] = i cnt'[1] = i cnt[1]=i,所以 c n t [ 0 ] = i − 1 cnt[0] = i - 1 cnt[0]=i1 c n t [ 1 ] = i − 2 cnt[1] = i - 2 cnt[1]=i2 c n t ′ [ 1 ] = i cnt'[1] = i cnt[1]=i c n t ′ [ 0 ] = i − 1 cnt'[0] = i - 1 cnt[0]=i1 也就是在 a i ≠ a i − 1 a_i \not = a_{i - 1} ai=ai1 时只有唯一的一种好扩展。

  • C a s e   2 Case \space 2 Case 2 a i = a i − 1 a_i = a_{i - 1} ai=ai1

  • 我们还是假设 a i = a i − 1 = 1 a_i = a_{i - 1} = 1 ai=ai1=1。若前面的所有数都是 1 1 1,那么无论在 i − 1 i - 1 i1 个空位上怎么填,都能满足 1 ⩽ j ⩽ i ,   c n t [ 1 ] ⩾ j 1 \leqslant j \leqslant i, \space cnt[1] \geqslant j 1ji, cnt[1]j 的要求,所以方案数就是 2 i − 1 2^{i - 1} 2i1。若前面存在一个左零右一,即 a j − 1 = 0 ,   a j = 1 a_{j - 1} = 0, \space a_j = 1 aj1=0, aj=1,那这个分界点处的情况就和 C a s e   1 Case \space 1 Case 1 完全一样,只有一种填法;而在这个 j j j 之后就都是 1 1 1了,也就是在剩下的 i − j − 1 i - j - 1 ij1 个空位中,无论怎么填也都能满足要求(这里和 C a s e   1 Case \space 1 Case 1 的推导一样),所以方案数就是 2 i − j − 1 2^{i - j - 1} 2ij1。将上述两种情况合起来,也就是求最后一段连续相等的数的个数 c n t cnt cnt,最后的方案数为 2 c n t − 1 2^{cnt - 1} 2cnt1

综上,最后的方案数只要按照上述所说的从左往右扫一遍相加即可(不要忘记取模)。

Code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
#include <cmath>
#include <unordered_set>
#include <set>
#include <unordered_map>
#include <map>
#include <stack>
#include <assert.h>

#define endl '\n'

#define x first
#define y second

#define ls u << 1
#define rs u << 1 | 1

#define l(x) tr[x].s[0]
#define r(x) tr[x].s[1]

#define pb push_back
#define ppb pop_back()
#define all(x) x.begin(), x.end()
#define debug(x) cout << (#x) << ' ' << x << endl

#define fup(i, a, b) for (int i = a; i <= b; i ++ )
#define fdn(i, a, b) for (int i = a; i >= b; i -- )

using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
typedef double db;
typedef long double LD;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
typedef pair<double, double> PDD;
typedef pair<string, int> PSI;
typedef pair<char, int> PCI;

const int N = 200010, M = N << 1, MOD = 998244353, INF = 0x3f3f3f3f;
const db eps = 1e-8;
const db PI = acos(-1);

const int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};

int n, m;
char s[N];

LL qmi(LL a, LL b)
{
    LL res = 1;
    while (b)
    {
        if (b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}

void solve()
{
    cin >> n >> s + 1;
    
    LL res = 0;
    int cnt = 0;
    for (int i = 1; i <= n; i ++ )
    {
        if (s[i] == s[i - 1]) res = (res + qmi(2, ++ cnt)) % MOD;
        else cnt = 0, res ++ ;
    }
    
    cout << (res % MOD + MOD) % MOD << endl;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    
    int T = 1;
    cin >> T;
    while (T -- ) solve();

    return 0;
}

D. GCD Queries

Problem Summary

这题是交互题,题意大致如下:

  1. 首先给定从 0 0 0 n − 1 n - 1 n1 的排列,设其为数组 q q q,现在要求你在查询次数不超过 2 n 2n 2n 次的限制下,找出一种查询方案,使得最后查询到的两个下标 x x x y y y 中有一个下标对应的数为 0 0 0,即 ∃ x ,   y \exists x, \space y x, y,使得 q [ x ] = 0 q[x] = 0 q[x]=0 q [ y ] = 0 q[y] = 0 q[y]=0

  2. 每次查询,需要向评测机发送 “ ?   x   y ? \space x \space y ? x y”(没有引号)这种格式的询问,评测机会发送回 g c d ( q [ x ] ,   q [ y ] ) gcd(q[x], \space q[y]) gcd(q[x], q[y]),也就是它们的最大公约数,你需要借助返回的最大公约数来确定 0 0 0 的位置。

  3. 最后需要提交答案,提交答案的格式为 “ !   x   y ! \space x \space y ! x y”(没有引号), 但如果询问次数超过 2 n 2n 2n 或者提交的两个下标中没有一个满足 q [ x ] = 0 q[x] = 0 q[x]=0 q [ y ] = 0 q[y] = 0 q[y]=0,那评测机会返回 − 1 -1 1,表示 W r o n g   A n s w e r Wrong \space Answer Wrong Answer,否则返回 1 1 1,表示询问次数合法且答案正确。

Solution

首先我们知道一些最大公约数的性质,其中对于这题最重要的就是 g c d ( 0 , x ) = x gcd(0, x) = x gcd(0,x)=x

  1. n = 2 n = 2 n=2,那么排列只有 [ 0 , 1 ] [0, 1] [0,1] [ 1 , 0 ] [1, 0] [1,0],所以直接提交 x = 1 ,   y = 2 x = 1, \space y = 2 x=1, y=2 即可,因为其中一定有一个对应的数为 0 0 0

  2. n ⩾ 3 n \geqslant 3 n3,那就先将两个指针放在开头,即 x = 1 ,   y = 2 x = 1, \space y = 2 x=1, y=2,然后从 i = 3 i = 3 i=3 开始往后询问,每一次询问我们都询问 a = g c d ( q [ x ] , q [ i ] ) a = gcd(q[x], q[i]) a=gcd(q[x],q[i]) b = g c d ( q [ i ] , q [ y ] ) b = gcd(q[i], q[y]) b=gcd(q[i],q[y]),比较 a a a b b b,同时保持 x ≠ y x \not = y x=y

    • a = b a = b a=b,那么一定有 q [ i ] ≠ 0 q[i] \not = 0 q[i]=0。反证法:若 q [ i ] = 0 q[i] = 0 q[i]=0,则 a = g c d ( q [ x ] , q [ i ] ) = q [ x ] a = gcd(q[x], q[i]) = q[x] a=gcd(q[x],q[i])=q[x] b = g c d ( q [ i ] , q [ y ] ) = q [ y ] b = gcd(q[i], q[y]) = q[y] b=gcd(q[i],q[y])=q[y],由于这是一个排列,所以当 x ≠ y x \not = y x=y,一定有 q [ x ] ≠ q [ y ] q[x] \not = q[y] q[x]=q[y],这就有 a ≠ b a \not = b a=b,矛盾,因此一定有 q [ i ] ≠ 0 q[i] \not = 0 q[i]=0,这说明 i i i 这个下标对应的数一定不是我们要找的,所以我们无需更新 x x x y y y

    • a < b a \lt b a<b,那么一定有 q [ x ] ≠ 0 q[x] \not = 0 q[x]=0。反证法:若 q [ x ] = 0 q[x] = 0 q[x]=0,则 a = g c d ( q [ x ] , q [ i ] ) = q [ i ] a = gcd(q[x], q[i]) = q[i] a=gcd(q[x],q[i])=q[i] b = g c d ( q [ i ] , q [ y ] ) b = gcd(q[i], q[y]) b=gcd(q[i],q[y])

      • q [ y ] = 0 q[y] = 0 q[y]=0,则 b = q [ i ] = a b = q[i] = a b=q[i]=a,一定不可能。

      • q [ y ] ≠ 0 q[y] \not = 0 q[y]=0,那么 b b b 一定要取 q [ i ] q[i] q[i] 的因数中小于 q [ i ] q[i] q[i] 的那部分(若等于,那就有 a = b a = b a=b,矛盾),因此 a = q [ i ] > b a = q[i] \gt b a=q[i]>b,同样和 a < b a \lt b a<b 矛盾。

      • 接下来说明 q [ i ] q[i] q[i] q [ y ] q[y] q[y] 可能为 0 0 0

        • q [ i ] = 0 q[i] = 0 q[i]=0,则 a = g c d ( q [ x ] , q [ i ] ) = q [ x ] a = gcd(q[x], q[i]) = q[x] a=gcd(q[x],q[i])=q[x] b = g c d ( q [ i ] , q [ y ] ) = q [ y ] b = gcd(q[i], q[y]) = q[y] b=gcd(q[i],q[y])=q[y],只要 q [ x ] < q [ y ] q[x] \lt q[y] q[x]<q[y] 就有 a < b a \lt b a<b
        • q [ y ] = 0 q[y] = 0 q[y]=0,则 a = g c d ( q [ x ] , q [ i ] ) a = gcd(q[x], q[i]) a=gcd(q[x],q[i]) b = g c d ( q [ i ] , q [ y ] ) = q [ i ] b = gcd(q[i], q[y]) = q[i] b=gcd(q[i],q[y])=q[i],则一定有 a ⩽ b a \leqslant b ab,并且只要 a a a q [ i ] q[i] q[i] 的因数中小于 q [ i ] q[i] q[i] 的那部分,就会有 a < b a \lt b a<b
      • 综上,一定有 q [ x ] ≠ 0 q[x] \not = 0 q[x]=0,并且在这次要把 x x x 换成有可能等于 0 0 0 的数的下标,也就是 i i i 或者 y y y,但是 x x x y y y 不能相同,所以令 x = i x = i x=i

    • a > b a \gt b a>b,推导过程和上述完全一致,结果就就是一定有 q [ y ] ≠ 0 q[y] \not = 0 q[y]=0,并且在这次要把 y y y 换成有可能等于 0 0 0 的数的下标,也就是 i i i 或者 x x x,但是 y y y x x x 不能相同,所以令 y = i y = i y=i

  3. 这样得到的 x x x y y y 一定有一个对应的数是 0 0 0

    • x x x y y y 在下标 1 1 1 2 2 2 处就有一个为 0 0 0,即 0 0 0 在遍历前就取到了,意味着等于 0 0 0 的那个数的下标自始至终都不会变化,因为我们只会变动一定有 q [ k ] ≠ 0 q[k] \not = 0 q[k]=0 的下标 k k k
    • 若一开始没有 q [ 1 ] = 0 q[1] = 0 q[1]=0 q [ 2 ] = 0 q[2] = 0 q[2]=0,那么至少有一次上述的改变,即变化 x x x y y y 为可能为 0 0 0 的下标,不可能在最后仍有 q [ x ] ≠ 0 q[x] \not = 0 q[x]=0 q [ y ] ≠ 0 q[y] \not = 0 q[y]=0,因为如果扫描一次仍是这样,说明没有遇到任何一次的 a < b a \lt b a<b 或者 a > b a \gt b a>b,也就是一定有从 3 3 3 开始的每个下标都有 q [ i ] ≠ 0 q[i] \not = 0 q[i]=0,这就说明整个排列都没有 0 0 0,与排列的定义矛盾。
Code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
#include <cmath>
#include <unordered_set>
#include <set>
#include <unordered_map>
#include <map>
#include <stack>
#include <assert.h>

// 记得一定要注释掉 endl 为 '\n' 的语句,因为要刷新缓冲区
// #define endl '\n'

#define x first
#define y second

#define ls u << 1
#define rs u << 1 | 1

#define l(x) tr[x].s[0]
#define r(x) tr[x].s[1]

#define pb push_back
#define ppb pop_back()
#define all(x) x.begin(), x.end()
#define debug(x) cout << (#x) << ' ' << x << endl

#define fup(i, a, b) for (int i = a; i <= b; i ++ )
#define fdn(i, a, b) for (int i = a; i >= b; i -- )

using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
typedef double db;
typedef long double LD;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
typedef pair<double, double> PDD;
typedef pair<string, int> PSI;
typedef pair<char, int> PCI;

const int N = 200010, M = N << 1, MOD = 998244353, INF = 0x3f3f3f3f;
const db eps = 1e-8;
const db PI = acos(-1);

const int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};

int n, m;
char s[N];

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

int submit(int x, int y)
{
    cout << "! " << x << ' ' << y << endl;
    int res;
    cin >> res;
}

void solve() 
{
    cin >> n;
    int x = 1, y = 2;
    for (int i = 3; i <= n; i ++ ) 
    {
        int a = query(x, i), b = query(i, y);
        if (a < b) x = i;
        if (a > b) y = i;
    }
    submit(x, y);
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    
    int T = 1;
    cin >> T;
    while (T -- ) solve();

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
抱歉,根据提供的引用内容,我无法理解你具体想要问什么问题。请提供更清晰明确的问题,我将竭诚为你解答。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Codeforces Round 860 (Div. 2)题解](https://blog.csdn.net/qq_60653991/article/details/129802687)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [【CodeforcesCodeforces Round 865 (Div. 2) (补赛)](https://blog.csdn.net/t_mod/article/details/130104033)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Codeforces Round 872 (Div. 2)(前三道](https://blog.csdn.net/qq_68286180/article/details/130570952)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值