由于不含 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 ) 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 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  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  
显然,一个合法序列的连续子序列也是合法的。一个O  ( n 2 ) \operatorname{O}(n^{2}) O ( n 2 ) a x a_{x} a x  x x x [ L x , R x ] [L_{x},R_{x}] [ L x  , R x  ] 
下面优化此过程,期望在O  ( n log  n ) \operatorname{O}(n\log n) O ( n log  n ) L , R L,R L , R 
把每个部分拆开看,先对于每个x x x i i i [ i , x ] [i,x] [ i , x ] a x a_{x} a x