AGC Strange Sorting 题解
Blue_Fish_Junly · · 题解
我不知道啊,他们说有思维题我就来了。
#
我们先来看操作到底在做什么。每一步操作,其实是把序列里所有的“前缀最大值”找出来,保持它们原来的顺序,整体剪切到序列的末尾。前缀最大值就是那些“从开头到现在为止的最大值刚好是自己”的位置。这个观察很重要,因为一旦一个数被确认为前缀最大值,它在当前这一步就一定会被扔到后面去。
接下来就是思维转折点。一个元素是不是前缀最大值,只取决于它前面有没有比它大的数。因此,数值较小的元素无论怎么移动,都不会影响数值较大的元素的判定。换句话说,如果我们只关心数值在某个区间内的那些元素,完全可以暂时忽略比它们都小的元素,剩下的那部分序列的高低项操作效果,和在整个序列上操作后只看这部分的结果是一样的。
这给了我们一个机会:可以把问题按数值从大到小一层一层地剥离。一开始只考虑最大的那个数
你可能会发现,在我们所关心的大数字集合里,它们永远不会是完全乱序的,而是处于一种“循环有序”的状态。想象把这些数字按值从小到大列出来,它们在序列里的位置正好是一个轮换顺序,比如
为了刻画当前这个循环有序结构,我们维护一个代表元素
反之,如果这三个位置不是循环递增的,那就意味着现在这个结构装不下
如果当前操作次数还是
于是整个算法就变得非常干净,时间复杂度
#include<bits/stdc++.h>
using namespace std;
int n,now,pos[200002],ans,lst;
signed main() {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>n;
for(int i=1; i<=n; i++) cin>>now, pos[now]=i;
for(int i=n-1; i>=1; i--) {
int cnt=(pos[lst]<pos[i])+(pos[i]<pos[i+1])+(pos[i+1]<pos[lst]);
if(ans and cnt>=2) continue;
if(!ans and pos[i]<pos[i+1]) continue;
ans++,lst=i+1;
}
cout<<ans;
return 0;
}