#P2955. 第2题-好位置
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 42
            Accepted: 5
            Difficulty: 7
            
          
          
          
                       所属公司 : 
                              阿里
                                
            
                        
              时间 :2025年5月12日-阿里国际(开发岗)
                              
                      
          
 
- 
                        算法标签>构造          
 
第2题-好位置
题解
问题描述
给定一个长度为 n 的排列 p=[p1,p2,…,pn]。称下标 i(1≤i<n)为“好位置”,如果存在 j 满足 i<j≤n 且 pi>pj。记原排列中“好位置”的个数为 G(p)。
要求构造另一个排列 q,满足:
- q=p;
 - G(q)=G(p)。
 
若存在,输出「YES」及一个满足条件的 q;否则输出「NO」。
思路
- 
特殊情况:当 n<3 时,排列长度太小,任意交换都会改变“好位置”数,因此无解,直接输出「NO」。
 - 
寻找最右侧下降点:遍历下标 i 从 n−1 到 1(即 i=n−2,n−3,…,1),寻找第一个满足 pi>pi+1 的下标,记为 idx。如果不存在这样的下标,则原排列完全递增,无法构造不同且保持相同“好位置”数的 q,输出「NO」。
 - 
局部交换构造:将 p 复制为 q,然后:
- 如果 idx≤n−3,交换 q[idx] 与 q[idx+2];
 - 否则(即 idx=n−2),交换 q[idx−1] 与 q[idx]。
 
这样仅在最右侧下降点附近进行一次交换,不会改变其他位置的“好位置”状态,且得到 q=p。
 - 
输出答案:如上构造成功,则打印「YES」和 q;否则打印「NO」。
 
C++
#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.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];  // 读入原排列 p
        }
        // 情况1:n < 3 无解
        if (n < 3) {
            cout << "NO\n";
            continue;
        }
        // 寻找最右侧下降点 idx,使得 a[idx] > a[idx+1]
        int idx = -1;
        for (int i = n - 2; i >= 0; --i) {
            if (a[i] > a[i + 1]) {
                idx = i;
                break;
            }
        }
        // 如果没有下降点,排列已全增,无解
        if (idx < 0) {
            cout << "NO\n";
            continue;
        }
        // 复制为 q 并局部交换
        vector<int> b = a;
        if (idx <= n - 3) {
            // 交换 b[idx] 与 b[idx+2]
            swap(b[idx], b[idx + 2]);
        } else {
            // idx == n-2,交换 b[idx-1] 与 b[idx]
            swap(b[idx - 1], b[idx]);
        }
        // 输出结果
        cout << "YES\n";
        for (int i = 0; i < n; ++i) {
            cout << b[i] << (i + 1 < n ? ' ' : '\n');
        }
    }
    return 0;
}
Python
import sys
input = sys.stdin.readline
def solve():
    T = int(input())  # 测试用例数量
    for _ in range(T):
        n = int(input())  # 排列长度
        p = list(map(int, input().split()))  # 读入原排列 p
        # 情况1:n < 3 无解
        if n < 3:
            print("NO")
            continue
        # 寻找最右侧下降点 idx,使得 p[idx] > p[idx+1]
        idx = -1
        for i in range(n - 2, -1, -1):
            if p[i] > p[i + 1]:
                idx = i
                break
        # 如果没有下降点,排列已全增,无解
        if idx < 0:
            print("NO")
            continue
        # 构造 q 并局部交换
        q = p.copy()
        if idx <= n - 3:
            # 交换 q[idx] 与 q[idx+2]
            q[idx], q[idx + 2] = q[idx + 2], q[idx]
        else:
            # idx == n-2,交换 q[idx-1] 与 q[idx]
            q[idx - 1], q[idx] = q[idx], q[idx - 1]
        # 输出结果
        print("YES")
        print(*q)
if __name__ == '__main__':
    solve()
Java
import java.io.*;
import java.util.*;
public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int T = Integer.parseInt(br.readLine());  // 测试用例数量
        while (T-- > 0) {
            int n = Integer.parseInt(br.readLine());  // 排列长度
            int[] a = Arrays.stream(br.readLine().split(" "))
                            .mapToInt(Integer::parseInt)
                            .toArray();  // 读入原排列 p
            // 情况1:n < 3 无解
            if (n < 3) {
                System.out.println("NO");
                continue;
            }
            // 寻找最右侧下降点 idx,使得 a[idx] > a[idx+1]
            int idx = -1;
            for (int i = n - 2; i >= 0; --i) {
                if (a[i] > a[i + 1]) {
                    idx = i;
                    break;
                }
            }
            // 如果没有下降点,排列已全增,无解
            if (idx < 0) {
                System.out.println("NO");
                continue;
            }
            // 构造 q 并局部交换
            int[] b = a.clone();
            if (idx <= n - 3) {
                // 交换 b[idx] 与 b[idx+2]
                int tmp = b[idx];
                b[idx] = b[idx + 2];
                b[idx + 2] = tmp;
            } else {
                // idx == n-2,交换 b[idx-1] 与 b[idx]
                int tmp = b[idx - 1];
                b[idx - 1] = b[idx];
                b[idx] = tmp;
            }
            // 输出结果
            System.out.println("YES");
            for (int x : b) System.out.print(x + " ");
            System.out.println();
        }
    }
}
        题目内容
小苯定义一个数组的“好位置”为:满足其右侧存在比其小的元素形式化的即:
在数组a中对于1≤i<n存在1≤i<j≤n使得ai>aj,则称i为“好位置”。
现在小苯有一个长度为n 的排列p,他希望你构造一个长为n的排列q,满足p=q同时p,q的“好位置”个数相同。
输入描述
本题包含多组测试数据。
第一行一个正整数T(1≤T≤10000),表示测试数据的组数。
接下来对于每组测试数据,输入包含两行
第一行一个正整数n(1≤n≤200000),表示排列p的长度。
第二行n个正整数pi(1≤pi≤n),表示排列p。(保证输入是一个排列)
(保证所有测试数据中,n的总和不超过200000)
输出描述
输出包含T行,对于每组测试数据输出一行。
如果可以找到一个满足条件的q,则先输出一行一个字符串“YES”(不含双引号),表示可以找到,再在下一行输出一行n个数字表示满足条件的排列q(有多解输出任意即可。)
如果找不到一个满足条件的q,则输出一个“NO”(不含双引号)。
样例1
输入
2 
4
2 1 3 4
3 
1 2 3
输出
YES
1 2 4 3
NO
说明
对于第一个测试数据:
[2,1,3,4]的"好位置”个数为1。(好位置有:i=1)
[1,2,4,3]的“好位置”个数也为1。(好位置有:i=3)