由于不含 0 的网格的 MEX 都是 0,希望更多的网格包括 0,所以 0 放在正中间。数越小就要越靠近中心(严格证明可以看看官方题解),可以构造
1 2 3 4 9 8 7 6 10 1 0 5 11 2 3 4 12 13 14 15
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 #include <bits/stdc++.h> using namespace std;int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ), cout.tie (nullptr ); int t; cin >> t; while (t--) { int n; cin >> n; vector a (n, vector<int >(n)) ; int x = (n + 1 ) / 2 - 1 ; int y = (n + 1 ) / 2 - 1 ; int cur = n % 2 ; for (int d = n % 2 + 1 ; d <= n; d += 2 ) { x++; y++; for (auto [dx, dy] : {array{-1 , 0 }, {0 , -1 }, {1 , 0 }, {0 , 1 }}) { for (int _ = 0 ; _ < d; _++) { x += dx; y += dy; a[x][y] = cur++; } } } for (int i = 0 ; i < n; i++) { for (int j = 0 ; j < n; j++) { cout << a[i][j] << " " ; } cout << endl; } cout << endl; } return 0 ; }
首先元素位置的奇偶性不会变。样例告诉我们大概率是可以自由调整的,调整为奇序列升序,偶序列升序。
但有个例外,考虑交换的本质是逆序对 +1/-1,因此整个排列的逆序对 +2/0/-2,即奇偶性总是不变。如果排序后,整个序列逆序对的奇偶性不对了,就需要交换最后一项和倒数第三项。
时间复杂度O ( n log n ) \operatorname{O}(n\log n) O ( n log n ) 。
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 #include <bits/stdc++.h> using namespace std;int cal (const vector<int >& a) { int n = a.size (); vector<int > vis (n) ; int res = 0 ; for (int i = 0 ; i < n; i++) { if (vis[i]) continue ; int cnt = 0 ; for (int u = i; !vis[u]; u = a[u]) { vis[u] = true ; cnt++; } res += cnt - 1 ; } return res; } int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ), cout.tie (nullptr ); int t; cin >> t; while (t--) { int n; cin >> n; vector<int > a (n) ; for (int i = 0 ; i < n; i++) { cin >> a[i]; a[i]--; } int cnt1 = cal (a); ranges::sort (a | views::drop (0 ) | views::stride (2 )); ranges::sort (a | views::drop (1 ) | views::stride (2 )); int cnt2 = cal (a); if ((cnt1 - cnt2) % 2 ) { swap (a[n - 1 ], a[n - 3 ]); } for (int i = 0 ; i < n; i++) { cout << ++a[i] << " \n" [i == n - 1 ]; } } return 0 ; }
好题。
题意 定义d x ( c ) d_x(c) d x ( c ) 为序列c c c 中最前面的x x x 和最后面的x x x 之间的距离,如果序列中未出现x x x ,或者只出现一次,定义d x ( c ) d_x(c) d x ( c ) 为 0。给定序列a a a ,找到一个等长序列b b b 满足1 ≤ b i ≤ a i 1\le b_i\le a_i 1 ≤ b i ≤ a i 。最大化∑ i = 1 n d i ( b ) \sum\limits_{i=1}^n d_i(b) i = 1 ∑ n d i ( b ) 。
Step 1 考察最优序列b b b 的形态
需要让尽可能多的值v v v 贡献d v ( b ) d_v(b) d v ( b ) ,并且每个贡献的d v ( b ) d_v(b) d v ( b ) 尽可能大。对于b b b 的构造方法,有一些显然的贪心:
每个对答案有贡献的值在序列b b b 中恰好出现两次; 考虑到约束b i ≤ a i b_i \le a_i b i ≤ a i ,较小的值更容易满足v ≤ a i v \le a_i v ≤ a i ,因此,如果我们选择k k k 个值,应该选择1 , 2 , … , k 1, 2, \dots, k 1 , 2 , … , k ; 将这k k k 个值尽可能放置在序列b b b 的最左边以及最右边。 例如,如果k = 3 k=3 k = 3 ,b b b 可能是b = ( 3 , 1 , x , 2 , x , x , 3 , 2 , x , 1 ) b = (3,1,x,2,x,x,3,2,x,1) b = ( 3 , 1 , x , 2 , x , x , 3 , 2 , x , 1 ) ,其中x x x 代表不贡献答案的值。
Step 2 对于固定的k k k ,为了构造出合法的b b b ,考察a a a 要满足的条件
假设我们已经确定要用值1 , 2 , … , k 1, 2, \dots, k 1 , 2 , … , k 来产生贡献。
我们需要在序列a a a 的一个尽可能短的前缀a 1 , … , a L k a_1, \dots, a_{L_k} a 1 , … , a L k 中找到k k k 个不同的位置,并在这些位置上放置1 , 2 , … , k 1, 2, \dots, k 1 , 2 , … , k ,同时满足b i ⩽ a i b_{i} \leqslant a_{i} b i ⩽ a i 的条件。类似地,也要找到尽可能短的后缀a R k , … , a n a_{R_k}, \dots, a_n a R k , … , a n 。
为了确保这2 k 2k 2 k 个位置是两两不同的,必须L k < R k L_k < R_k L k < R k 。
k k k 递增时L k L_{k} L k 递增,R k R_{k} R k 递减,两指针移动的次数为线性,可以枚举k k k 。
Step 3 枚举k k k ,求解答案
设左指针当前位于ℓ \ell ℓ 。
方法:开一个桶,对于当前a ℓ a_{\ell} a ℓ ,需要找到一个⩽ a ℓ \leqslant a_{\ell} ⩽ a ℓ 且桶里没有的最大元素x x x (这可以用 set 或并查集实现),如果找不到这样的x x x ,就继续移动指针ℓ \ell ℓ ,直到找到为止,然后把x x x 加入桶中。
正确性:桶中元素互不相同,且元素数量为k k k ,而且保证桶中元素分别大于等于1 , 2 , … , k 1,2,\dots,k 1 , 2 , … , k ,满足我们的需求。
时间复杂度O ( n ) \operatorname{O}(n) O ( n ) 或O ( n log n ) \operatorname{O}(n\log n) O ( n log n ) 。
并查集实现:
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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 #include <bits/stdc++.h> using namespace std;using ll = long long ;struct DisjointSets { vector<int > p; int tot; DisjointSets (int n) : p (n, -1 ), tot (n) {} int size (int u) { return -p[find (u)]; } int find (int u) { return p[u] < 0 ? u : p[u] = find (p[u]); } bool same (int u, int v) { return find (u) == find (v); } bool uno (int u, int v) { if (u = find (u), v = find (v); u == v) return false ; return p[u] += p[v], p[v] = u, tot--; } }; int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ), cout.tie (nullptr ); int t; cin >> t; while (t--) { int n; cin >> n; vector<int > a (n) ; for (int i = 0 ; i < n; i++) { cin >> a[i]; } int l = 0 , r = n - 1 ; vector<bool > visl (n + 1 ) , visr (n + 1 ) ; DisjointSets dsul (n + 1 ) , dsur (n + 1 ) ; ll res = 0 ; while (l < r) { while (l + 1 < n && (visl[a[l]] && dsul.find (a[l]) == 0 )) { l++; } int nl = dsul.find (a[l]); if (nl == 0 ) { break ; } visl[nl] = true ; dsul.uno (nl - 1 , nl); while (r > 0 && (visr[a[r]] && dsur.find (a[r]) == 0 )) { r--; } int nr = dsur.find (a[r]); if (nr == 0 ) { break ; } visr[nr] = true ; dsur.uno (nr - 1 , nr); if (l >= r) { break ; } res += r - l; l++, r--; } cout << res << endl; } return 0 ; }
set 实现:
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 #include <bits/stdc++.h> using namespace std;using ll = long long ;int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ), cout.tie (nullptr ); int t; cin >> t; while (t--) { int n; cin >> n; vector<int > a (n) ; for (int i = 0 ; i < n; i++) { cin >> a[i]; a[i]--; } auto range = views::iota (0 , n); set<int > sl (range.begin(), range.end()) , sr = sl; int l = 0 , r = n - 1 ; ll res = 0 ; while (l < r) { while (l < n && sl.upper_bound (a[l]) == sl.begin ()) { l++; } if (l == n) { break ; } sl.erase (--sl.upper_bound (a[l])); while (r >= 0 && sr.upper_bound (a[r]) == sr.begin ()) { r--; } if (r < 0 ) { break ; } sr.erase (--sr.upper_bound (a[r])); if (l >= r) { break ; } res += r - l; l++, r--; } cout << res << endl; } return 0 ; }
合法的序列满足:LIS 和 LDS 恰好有一个公共元素a x a_{x} a x ,且 LIS 和 LDS 取遍所有元素。即x x x 左边的元素中比a x a_{x} a x 小的递增,比a x a_{x} a x 大的递降,右边的元素中比a x a_{x} a x 大的递增,比a x a_{x} a x 小的递降。
把每个部分拆开看,先对于每个x x x ,求最小的i i i ,满足[ i , x ] [i,x] [ i , x ] 中比a x a_{x} a x 小的递增。
一一比较区间中所有元素太慢了,能否少比较一些呢?发现所求具有递推性,设x x x 左边最近的比a x a_{x} a x 小的数是a y a_{y} a y (可用单调栈求得),如果对于y y y ,满足[ i , y ] [i,y] [ i , y ] 中比a y a_{y} a y 小的递增,那么对于x x x ,一定满足[ i , x ] [i,x] [ i , x ] 中比a x a_{x} a x 小的递增。