QOJ #10977. Hidden Sequence Rotation 题解

Description

这是一个交互题(即你的程序需要通过标准输入/输出与评测器交互)。

你将得到一个整数 NN。评测器内部维护了一个长度为 NN隐藏序列 A=(A0,A1,,AN1)A = (A_0, A_1, \dots, A_{N-1}),其中每个元素都是介于 1110510^5 的整数。

注意: 题目中所有的数组下标都是从 0 开始的。

对于任意的 s=0,1,,N1s = 0, 1, \dots, N-1l=1,2,,Nl = 1, 2, \dots, N,我们定义一个序列 A(s,l)A(s, l),如下:

  • A(s,l)A(s, l) 是一个长度为 ll 的序列,它的第 ii 个元素是 A(s+i)modNA_{(s+i)\bmod N},即从位置 ss 开始,按模 NN 循环地取 ll 个元素。

你最多可以向评测器提出 20 次查询,每次查询的格式如下:

  • 你输出一组整数对 ((s0,l0),(s1,l1),,(sk1,lk1))((s_0, l_0), (s_1, l_1), \dots, (s_{k-1}, l_{k-1})),必须满足以下约束条件:

    • 1kN1 \leq k \leq N
    • 0siN10 \leq s_i \leq N - 1
    • 1liN1 \leq l_i \leq N
    • i=0k1liN\sum_{i=0}^{k-1} l_i \leq N

评测器会返回一组下标 ii(满足 0i<k0 \leq i < k),表示这些 A(si,li)A(s_i, l_i)字典序最小的那些。也就是说,它返回的是集合:

{iA(si,li)=min0j<kA(sj,lj)}\left\{i \mid A(s_i, l_i) = \min_{0 \leq j < k} A(s_j, l_j) \right\}

你需要通过最多 20 次这样的查询,找出所有满足 A(s,N)A(s, N) 在所有 A(s,N)A(s', N)字典序最小ss,即:

{s0s<N, A(s,N)=min0s<NA(s,N)}\left\{s \mid 0 \leq s < N,\ A(s, N) = \min_{0 \leq s' < N} A(s', N) \right\}

1n1051\leq n\leq 10^5

Solution

首先如果给定 aa 序列,很容易想到倍增进行排序。

即假设现在已经求出满足 A(i,len)A(i,len) 最小的坐标后,设其为 i1,i2,,iki_1,i_2,\ldots,i_k,现在只需要再求出 i1+len,i2+len,,ik+leni_1+len,i_2+len,\ldots,i_k+len 的最小坐标后就可以得到 2len2\cdot len 的坐标。

回到这题,同样的,先询问一遍 (0,1),(1,1),,(n1,1)(0,1),(1,1),\ldots,(n-1,1) 后可以得到 len=1len=1 的最小坐标。

然后每次按照上面的那个做法询问 (ij+len,len)(i_j+len,len) 的结果进行倍增。但是这么做 li\sum l_i 的长度和可能大于 nn

注意到如果 [ij,ij+len1][i_j,i_j+len-1][ij+1,ij+1+len1][i_{j+1},i_{j+1}+len-1] 有交,则 d=ij+1ijd=i_{j+1}-i_j 一定是 lenlen 的因数,且一定存在一个最小下标是 ij+lendi_j+len-d

容易发现如果把相邻有交的区间连一条边,如果只有一个连通块,则说明已经排序好了,因为序列周期已经找到了。否则只有每个连通块最左边的区间可能成为答案,感性理解这是对的。

所以只有 ijij1ki_j-i_{j-1}\geq k 的下标需要询问,长度之和也就控制在了 nn 以内。

总次数显然是 logn\log n

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 1e5 + 5;

int n;

std::vector<int> ask(std::vector<int> &a, int len) {
std::cout << "? " << a.size() << '\n';
for (auto x : a) std::cout << x << ' ' << len << '\n';
std::cout.flush();
std::vector<int> v;
int k;
std::cin >> k;
for (int i = 1; i <= k; ++i) {
int x;
std::cin >> x;
v.emplace_back(a[x]);
}
return v;
}

void dickdreamer() {
std::cin >> n;
std::vector<int> a;
for (int i = 0; i < n; ++i) a.emplace_back(i);
a = ask(a, 1);
for (int len = 1; len <= n; len <<= 1) {
if (a.size() == 1) break;
std::vector<int> tmp;
for (int i = 0; i < a.size(); ++i) {
if (i && a[i] - a[i - 1] > len || !i && a[i] - a.back() + n > len)
tmp.emplace_back((a[i] + len) % n);
}
if (!tmp.size()) break;
a = ask(tmp, len);
for (auto &x : a) x = (x - len + n) % n;
}
std::cout << "! " << a.size() << '\n';
for (auto x : a) std::cout << x << '\n';
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

QOJ #10977. Hidden Sequence Rotation 题解
https://sobaliuziao.github.io/2025/06/09/post/35ab403d.html
作者
Egg_laying_master
发布于
2025年6月9日
许可协议