Description这是一个交互题 (即你的程序需要通过标准输入/输出与评测器交互)。
你将得到一个整数 N N N 。评测器内部维护了一个长度为 N N N 的隐藏序列 A = ( A 0 , A 1 , … , A N − 1 ) A = (A_0, A_1, \dots, A_{N-1}) A = ( A 0 , A 1 , … , A N − 1 ) ,其中每个元素都是介于 1 1 1 到 1 0 5 10^5 1 0 5 的整数。
注意: 题目中所有的数组下标都是从 0 开始的。
对于任意的 s = 0 , 1 , … , N − 1 s = 0, 1, \dots, N-1 s = 0 , 1 , … , N − 1 和 l = 1 , 2 , … , N l = 1, 2, \dots, N l = 1 , 2 , … , N ,我们定义一个序列 A ( s , l ) A(s, l) A ( s , l ) ,如下:
A ( s , l ) A(s, l) A ( s , l ) 是一个长度为 l l l 的序列,它的第 i i i 个元素是 A ( s + i ) m o d N A_{(s+i)\bmod N} A ( s + i ) m o d N ,即从位置 s s s 开始,按模 N N N 循环地取 l l l 个元素。你最多可以向评测器提出 20 次查询 ,每次查询的格式如下:
你输出一组整数对 ( ( s 0 , l 0 ) , ( s 1 , l 1 ) , … , ( s k − 1 , l k − 1 ) ) ((s_0, l_0), (s_1, l_1), \dots, (s_{k-1}, l_{k-1})) ( ( s 0 , l 0 ) , ( s 1 , l 1 ) , … , ( s k − 1 , l k − 1 ) ) ,必须满足以下约束条件:
1 ≤ k ≤ N 1 \leq k \leq N 1 ≤ k ≤ N 0 ≤ s i ≤ N − 1 0 \leq s_i \leq N - 1 0 ≤ s i ≤ N − 1 1 ≤ l i ≤ N 1 \leq l_i \leq N 1 ≤ l i ≤ N ∑ i = 0 k − 1 l i ≤ N \sum_{i=0}^{k-1} l_i \leq N ∑ i = 0 k − 1 l i ≤ N 评测器会返回一组下标 i i i (满足 0 ≤ i < k 0 \leq i < k 0 ≤ i < k ),表示这些 A ( s i , l i ) A(s_i, l_i) A ( s i , l i ) 中字典序最小的那些 。也就是说,它返回的是集合:
{ i ∣ A ( s i , l i ) = min 0 ≤ j < k A ( s j , l j ) } \left\{i \mid A(s_i, l_i) = \min_{0 \leq j < k} A(s_j, l_j) \right\} { i ∣ A ( s i , l i ) = 0 ≤ j < k min A ( s j , l j ) }
你需要通过最多 20 次这样的查询,找出所有满足 A ( s , N ) A(s, N) A ( s , N ) 在所有 A ( s ′ , N ) A(s', N) A ( s ′ , N ) 中字典序最小 的 s s s ,即:
{ s ∣ 0 ≤ s < N , A ( s , N ) = min 0 ≤ s ′ < N A ( s ′ , N ) } \left\{s \mid 0 \leq s < N,\ A(s, N) = \min_{0 \leq s' < N} A(s', N) \right\} { s ∣ 0 ≤ s < N , A ( s , N ) = 0 ≤ s ′ < N min A ( s ′ , N ) }
1 ≤ n ≤ 1 0 5 1\leq n\leq 10^5 1 ≤ n ≤ 1 0 5 。
Solution首先如果给定 a a a 序列,很容易想到倍增进行排序。
即假设现在已经求出满足 A ( i , l e n ) A(i,len) A ( i , l e n ) 最小的坐标后,设其为 i 1 , i 2 , … , i k i_1,i_2,\ldots,i_k i 1 , i 2 , … , i k ,现在只需要再求出 i 1 + l e n , i 2 + l e n , … , i k + l e n i_1+len,i_2+len,\ldots,i_k+len i 1 + l e n , i 2 + l e n , … , i k + l e n 的最小坐标后就可以得到 2 ⋅ l e n 2\cdot len 2 ⋅ l e n 的坐标。
回到这题,同样的,先询问一遍 ( 0 , 1 ) , ( 1 , 1 ) , … , ( n − 1 , 1 ) (0,1),(1,1),\ldots,(n-1,1) ( 0 , 1 ) , ( 1 , 1 ) , … , ( n − 1 , 1 ) 后可以得到 l e n = 1 len=1 l e n = 1 的最小坐标。
然后每次按照上面的那个做法询问 ( i j + l e n , l e n ) (i_j+len,len) ( i j + l e n , l e n ) 的结果进行倍增。但是这么做 ∑ l i \sum l_i ∑ l i 的长度和可能大于 n n n 。
注意到如果 [ i j , i j + l e n − 1 ] [i_j,i_j+len-1] [ i j , i j + l e n − 1 ] 和 [ i j + 1 , i j + 1 + l e n − 1 ] [i_{j+1},i_{j+1}+len-1] [ i j + 1 , i j + 1 + l e n − 1 ] 有交,则 d = i j + 1 − i j d=i_{j+1}-i_j d = i j + 1 − i j 一定是 l e n len l e n 的因数,且一定存在一个最小下标是 i j + l e n − d i_j+len-d i j + l e n − d 。
容易发现如果把相邻有交的区间连一条边,如果只有一个连通块,则说明已经排序好了,因为序列周期已经找到了。否则只有每个连通块最左边的区间可能成为答案,感性理解这是对的。
所以只有 i j − i j − 1 ≥ k i_j-i_{j-1}\geq k i j − i j − 1 ≥ k 的下标需要询问,长度之和也就控制在了 n n n 以内。
总次数显然是 log n \log n log n 。
Code1 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> 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 ; while (T--) dickdreamer (); return 0 ; }