Codeforces Round 874 (Div. 3) D. Flipper

题目大意

给你一个permutation,你能选择一个区间,将区间[l,r]内的数翻转,然后交换前缀[1,l) 和 后缀 (r,n],问你怎么交换获得字典序最大的permutation.

思考1:如何获得最大的字典序最大的permutation

答案1 贪心地将最大的数放在最前面

思考2:我可以控制哪几个位置的数

答案2 开头,开头后若干个位置的一个(稍后解释),

思考3:我一定能把permutation里最大的那个放在开头吗

答案3 只能将下标[2,n]中最大的数放在开头

思考4:我是否是将整个permutation分成了几块输出

答案4 是,分两种情况:3段或2段

solution:

观察可知,我们只需要将r设为最大的数前面一位就能将最大的数放在开头.

特殊1:最大的数在开头.

此时无论我们怎么操作最大的数会向后移动,所以转为考虑下表[2,n]最大的数放在开头

特殊2:最大的数在结尾.

此时我们并不一定要交换前面的数可以交换[n,n]使最大的数放在第一位


所以我们只要分类讨论这两种情况就好了(以下统称下标[2,n]最大的数为最大的数)

考虑情况1:最大的数在[2,n)

对于选定的[l,r]我们一定是将r放在最大的数前面,经过变换后(r,n]的数被放在了开头,此时考虑第(n-r+1)位的数,显然这位是由l决定的(思考2中的第二个数)

经过转换的序列与原序列的关系如下图

  • 注意点:a[r]一定是在①段后输出的第一个数
  • 注意点2:l可以覆盖1

显然我们将l放置在从r往前数连续大于a[1]的最前面一个,也就是(1,r)中的一个

所以情况1只要确定这两个位置也就确定了l,r.所以我们可以直接按序输出(r,n],[l,r] (reverse),[1,l)的数即可(总共输出三个区间)

考虑情况2:最大的数在 位置n

只比情况1多了交换区间可能是[n,n]的可能,此时变换如下图

所以就情况2就相当于a[r]不一定是在①段后输出的第一个,也就是输出的三个区间里面后两个的顺序比较下

即先输出(r,n] 然后确定l的位置,最后确定先输出[1,l) 还是先输出[l,r] (reverse)

至此该题O(n)即可解决

可能讲得有点乱,请谅解

AC代码:

#include <bits/stdc++.h>
typedef long long ll;
const int N = 1e5 + 100 , M = 1e5 + 100;
const int INF = 1e9;
const int MOD = 1;
int n,m;
int a[ N ];
void solve(){
    std::cin>>n;
    for( int i = 1 ; i <= n ; i ++ ) std::cin>>a[ i ];
    int p = 2;
    for( int i = 2 ; i <= n ; i ++ ){
        if( a[ i ] > a[ p ] ) p = i;//下标[2,n]最大的数在哪
    }
    if( p == n ){
        for( int i = p ; i <= n ; i ++ ){
            std::cout<<a[ i ]<<' ';//输出(r,n]区间的数
        }
        p--;
        while( a[ p ] > a[ 1 ] ){
            std::cout<<a[ p ] <<' ';
            p--;
        }
        for( int i = 1 ; i <= p ; i ++ ){
            std::cout<<a[ i ]<<' ';
        }
        std::cout<<'\n';        
    }
    else{
        for( int i = p ; i <= n ; i ++ ){
            std::cout<<a[ i ]<<' ';//输出(r,n]区间的数
        }
        p--;
        std::cout<<a[ p ]<<' ';//a[r]一定是段①后输出的第一个数
        p--;
        while( a[ p ] > a[ 1 ] ){
            std::cout<<a[ p ]<<' ';//输出[l,r](reverse)区间的数
            p--;
        }
        for( int i = 1 ; i <= p ; i ++ ){
            std::cout<<a[ i ]<<' ';//输出[1,l)区间的数
        }

        std::cout<<'\n';
    }

}
int main() {
    std::ios::sync_with_stdio(0);std::cin.tie(0);std::cout.tie(0);
    ll t;std::cin>>t;
    while( t-- )
    {
        solve();
    }
}


posted @ 2023-05-22 23:43  lococ  阅读(64)  评论(0)    收藏  举报