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();
}
}
浙公网安备 33010602011771号