div1+2. 2178E - Flatten or Concatenate

Problem

This is an interactive problem.

While procrastinating at work, Dilhan the elf stumbled upon two arrays a a a and b b b. Initially, both of them consist of a single integer 2 k 2^k 2k (i.e., a = b = [ 2 k ] a = b = [2^k] a=b=[2k]), where k k k is a non-negative integer.

Dilhan then applied the following two types of operations an arbitrary number of times (possibly zero), in any order:

  1. Flatten --- Choose either a a a or b b b, and select any element x x x that is maximal within that array ( x x x does not need to be maximal in the other array). Then, replace x x x with two copies of x 2 \frac{x}{2} 2x in the same position. This operation can only be applied if x x x is even.
  2. Concatenate --- Set both a a a and b b b to be a + b a + b a+b, where + + + denotes array concatenation.

After performing these operations, Dilhan discards b b b, hides a a a from you, and challenges you to a game.

Let n n n be the length of the hidden array a a a. You may make the following query:

  • Choose an interval [ l , r ] [l, r] [l,r] ( 1 ≤ l ≤ r ≤ n 1 \leq l \leq r \leq n 1≤l≤r≤n), and Dilhan will tell you the sum a l + a l + 1 + ⋯ + a r a_l + a_{l+1} + \cdots + a_r al+al+1+⋯+ar.

Determine the value of the maximum element of a a a by making at most 300 queries.

Input

Each test contains multiple test cases. The first line contains the number of test cases t t t ( 1 ≤ t ≤ 100 1 \leq t \leq 100 1≤t≤100). The description of the test cases follows.

The first line of each test case contains a single integer n n n ( 1 ≤ n ≤ 10 5 1 \leq n \leq 10^5 1≤n≤105) --- the length of a a a.

It is guaranteed that 1 ≤ a i ≤ 2 30 1 \leq a_i \leq 2^{30} 1≤ai≤230, and the array a a a can be generated by the process described in the statements.

It is guaranteed that the sum of n n n over all test cases does not exceed 10 5 10^5 105.

Interaction

For each test case, you are first given an integer n n n --- the length of the hidden array a a a. You may then make up to 300 300 300 queries.

To make a query, print a line in the following format:

  • ? l r ( 1 ≤ l ≤ r ≤ n 1 \le l \le r \le n 1≤l≤r≤n) --- the indices chosen for the query.

After you print a query, the interactor will respond with a single integer --- the sum a l + a l + 1 + ⋯ + a r a_l + a_{l+1} + \dots + a_r al+al+1+⋯+ar.

To report that you have determined the value of the maximum element of a a a, print your answer in the following format:

  • ! m ( 1 ≤ m ≤ 2 30 1 \le m \le 2^{30} 1≤m≤230) --- the value of the maximum element.

Printing the answer does not count as one of the 300 300 300 queries.

Translation

这是一个交互式问题。

精灵 Dilhan 在工作摸鱼时偶然发现了两个数组 a a a 和 b b b。最初,这两个数组都只包含一个整数 2 k 2^k 2k(即 a = b = [ 2 k ] a = b = [2^k] a=b=[2k]),其中 k k k 是一个非负整数。

随后,Dilhan 以任意顺序进行了任意次数(可能为零次)的以下两种操作:

  1. 摊平 (Flatten) ------ 选择 a a a 或 b b b 中的一个,并选择该数组中最大的任意元素 x x x( x x x 不需要是另一个数组中的最大值)。然后,在相同位置将 x x x 替换为两个 x 2 \frac{x}{2} 2x。此操作仅在 x x x 为偶数时才能执行。
  2. 连接 (Concatenate) ------ 将 a a a 和 b b b 同时设为 a + b a + b a+b,其中 + + + 表示数组拼接。

在执行这些操作后,Dilhan 丢弃了 b b b,隐藏了数组 a a a,并向你发起挑战。

设 n n n 为隐藏数组 a a a 的长度。你可以进行以下查询:

  • 选择一个区间 [ l , r ] [l, r] [l,r] ( 1 ≤ l ≤ r ≤ n 1 \leq l \leq r \leq n 1≤l≤r≤n),Dilhan 会告诉你该区间的元素之和 a l + a l + 1 + ⋯ + a r a_l + a_{l+1} + \cdots + a_r al+al+1+⋯+ar。

请在最多 300 次查询内确定数组 a a a 中最大元素的值。

输入格式

每个测试包含多个测试用例。第一行包含测试用例的数量 t t t ( 1 ≤ t ≤ 100 1 \leq t \leq 100 1≤t≤100)。接下来是各测试用例的描述。

每个测试用例的第一行包含一个整数 n n n ( 1 ≤ n ≤ 10 5 1 \leq n \leq 10^5 1≤n≤105) ------ 数组 a a a 的长度。

题目保证 1 ≤ a i ≤ 2 30 1 \leq a_i \leq 2^{30} 1≤ai≤230,且数组 a a a 可以通过上述过程生成。

题目保证所有测试用例中 n n n 的总和不超过 10 5 10^5 105。

交互

对于每个测试用例,首先会给你一个整数 n n n ------ 隐藏数组 a a a 的长度。随后你最多可以进行 300 300 300 次查询。

要进行查询,请按以下格式打印一行:

  • ? l r ( 1 ≤ l ≤ r ≤ n 1 \le l \le r \le n 1≤l≤r≤n) ------ 为查询选择的索引。

在你打印查询后,交互器将返回一个整数 ------ 即总和 a l + a l + 1 + ⋯ + a r a_l + a_{l+1} + \dots + a_r al+al+1+⋯+ar。

当你确定了数组 a a a 中最大元素的值后,按以下格式打印你的答案:

  • ! m ( 1 ≤ m ≤ 2 30 1 \le m \le 2^{30} 1≤m≤230) ------ 最大元素的值。

打印答案不计入 300 300 300 次查询之内。

Solutions

Approach 1

分治。

后半部分对于分治递归算法的设计和 Good Bye 2025 Editorial - Codeforces 题解差不多。下述大致思路。

首先,可以发现两个最基础的结论:

  1. 操作 1 不影响数组 a 的和。
  2. 操作 2 会让数组 a 的和翻倍。

在此基础上,因为对于题目中序列操作次数的不可知 (也就是基本不可能用在解题思路中),可以发现将 a 从初始的 2 k + 1 2^{k+1} 2k+1 变为 2 k , 2 k , 2 k , 2 k 2^k,2^k,2^k,2^k 2k,2k,2k,2k 有两种方案:可以先操作 1 再 2 ,或是先 2 再两次 1 。

再根据上述结论 2 ,这启发我们,可以将所有操作 2 移到最前面,并仅视为对初始 a 的翻倍 (即所有操作 2 的对 a 的影响可以用将 a 翻倍 + 额外的操作 1 等价实现,这很直观,也很显然是对的) 。

于是,假定所有操作 2 已经进行完,那么,全局只剩操作 1 可以使用。

问题变为从一个未知的 2 r 2^r 2r 开始,每次只能使用操作 1 ,其余不变。

于是通过查询 1 ∼ n 1 \sim n 1∼n 可以得到 2 r 2^r 2r 的值,进而,可以通过二分查询到每一次操作 1 的分界线在哪里。

接下来的核心点在于题目中的操作要求 ------ 分割的是"最大元素"。于是可以得到一个结论,当确定一个分界线后,哪边的长度更短,最大值就在哪边。


2026年1月2日01:17:27

好像也可以这么想:初始时,a 数组就是一个值为 2 k 2^k 2k 的树根,而一次操作 1 就是任选一个最浅的叶子节点,将两个值为一半的节点连上 (或者理解为分裂出两个值减半的子节点) ,而操作 2 就是从根复制一遍树移到旁边,然后新建一个根链接原来的两个根且值翻倍。这样,构成了一颗二叉树,且最后的序列 a 就是叶子节点从左向右构成的,而且,此时每一个节点的值就是子树节点的值的和。

这种等价下,上述的操作可能就会更容易理解了。

Code
cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

std::mt19937 rng(std::random_device{}());
typedef long double ld;
typedef long long ll;
typedef unsigned long long ull;

const int mod = 998244353;
const double ept = 1e-9;

ll add(ll x, const ll&y) { x += y; if(x >= mod) { x -= mod; } return x; }
ll sub(ll x, const ll&y) { x -= y; if(x < 0) { x += mod; } return x; }
ll mul(ll x, const ll&y) { x = x * y; if(x >= mod) { x %= mod; } return x; }

#ifndef ONLINE_JUDGE
#define DEBUG 1
#define fastio
#define coutd cout
#define db(i) cout << #i << ": " << i << endl;
#define dbl(fmt, ...) fprintf(stdout, fmt, ##__VA_ARGS__);
#define dbi(vec) { cout << #vec << ": "; for(auto &v: vec) { cout << v << ' '; } cout << endl; }
#define dbv(vec, a, b) { cout << #vec << ": "; for(int i=a; i!=b+sig(b-a); i+=sig(b-a)) { cout << vec[i] << ' '; } cout << endl; }
#else
#define DEBUG 0
#define fastio ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define coutd cerr
#define db(...)
#define dbl(fmt, ...)
#define dbi(vec)
#define dbv(vec, a, b)
#endif

ll c[100100];

ll query(int r) {
  if(r == 0) return 0;
  if(c[r] > 0) return c[r];
  cout << "? " << 1 << ' ' << r << endl;
  cin >> c[r];
  return c[r];
}

void print(ll ans) {
  cout << "! " << ans << endl;
}

int n;

void dfs(int dl, int dr, ll pre, ll sum) {
  if(dl == dr) {
    print(sum);
    return;
  }
  else if(dl + 1 == dr) {
    print(sum / 2);
    return;
  }
  else if(dl + 2 == dr) {
    print(sum / 2);
    return;
  }

  int l = dl, r = dr, mid;
  while(l < r) {
    mid = l + (r-l) / 2;
    if(query(mid) - pre >= sum/2) r = mid;
    else l = mid + 1;
  }
  if(l - dl + 1 <= dr - l) dfs(dl, l, pre, sum/2);
  else dfs(l+1, dr, pre + sum / 2, sum / 2);
}

void solve(int T) {
  cin >> n;

  for(int i=1; i<=n; i++) c[i] = 0;

  ll sum = query(n);
  dfs(1, n, 0, sum);
}

void init() {}

signed main() {
    //freopen("1.in", "r", stdin);
    //freopen("1.out", "w", stdout);
    //cout.flags(ios::fixed); cout.precision(8);
    fastio
    init();
    int T_=1;
    std::cin >> T_;
    for(int _T=1; _T<=T_; _T++) { solve(_T); }
    return 0;
}
相关推荐
yu_anan1112 小时前
PPO/GRPO算法在RLHF中的实现
算法
leoufung2 小时前
Word Break:深度理解 DP 前缀结束点的核心思想
算法·word·动态规划
Aaron15882 小时前
三种主流接收机架构(超外差、零中频、射频直采)对比及发展趋势浅析
c语言·人工智能·算法·fpga开发·架构·硬件架构·信号处理
乐迪信息5 小时前
乐迪信息:目标检测算法+AI摄像机:煤矿全场景识别方案
人工智能·物联网·算法·目标检测·目标跟踪·语音识别
前端小L11 小时前
贪心算法专题(十):维度权衡的艺术——「根据身高重建队列」
javascript·算法·贪心算法
方得一笔11 小时前
自定义常用的字符串函数(strlen,strcpy,strcmp,strcat)
算法
Xの哲學11 小时前
Linux SMP 实现机制深度剖析
linux·服务器·网络·算法·边缘计算
wuk99812 小时前
使用PCA算法进行故障诊断的MATLAB仿真
算法·matlab
额呃呃12 小时前
二分查找细节理解
数据结构·算法