1 条题解
-
0
前言
做模拟赛做到的,手模一堆小样例模出来的。
思路
首先我们要知道一个结论:把一个排列通过交换相邻元素来得到恒等排序的最小交换次数等于这个排列的逆序对个数。
接着,我们要知道一个重要结论:。
::::info[证明] 定义 ,我们要观察一次相邻交换对 的影响。
假设我们交换相邻的两个元素 和 ,交换前两项 的和为:
交换后两项 的和为:
计算 的取值范围。
- 与 的差绝对值最大为 1。
- 与 的差绝对值最大为 1。
所以 的取值范围只能是 。
- 每进行一次相邻交换, 改变 1。
- 每进行一次相邻交换, 最多改变 2。
当我们通过 次交换将排列变为恒等排列时:
- 逆序对数从 变为 。
- 从 变为 。
由上可得 ,即 ::::
由此可知,在 时,对于每一次交换 必须满足 与 都向目标靠近,也就是不能存在无效的替换。
若排列中存在 这样的三元组,则该排列一定是不合法的,因为对于中间的 ,它肯定会被右边的 与左边的 所跨过,造成两次无效的替换。
方法
知道了上面的结论后就可以很简单的解决该问题了,由于需要处理所有循环移位,我们将原排列复制两遍,形成一个长度为 的数组 。
对于每一个位置 ,计算其左侧排列的最大值的位置 与其右侧排列的最小值位置 ,如果存在 且 ,那么就可以知道若一个长度为 的排列包含了区间 ,则此排列必不合法。设该排列的起始点为 ,则
- 。
- 。
即 。
使用差分数组记录所有使排列变“坏”的起始位置 。注意处理窗口在 序列上循环映射回 的逻辑。最终差分数组中值为 的位置即为好的起始点。
Code
#include<bits/stdc++.h> using namespace std; #define ll long long ll T; ll n; ll a[410000],l[410000],r[410000],d[410000]; void make(ll aa,ll bb){ if (aa>bb) return; ll x=(aa%n+n-1)%n+1,y=(bb%n+n-1)%n+1; if (x<=y){ d[x]++;d[y+1]--; }else{ d[x]++;d[n+1]--; d[1]++;d[y+1]--; } } void solve(){ cin>>n; for (int i=0;i<=2*n;i++){ a[i]=0;l[i]=-1;r[i]=-1;d[i]=0; } for (int i=1;i<=n;i++){ cin>>a[i]; a[i+n]=a[i]; } stack <int> q; for (int i=1;i<=2*n;i++){ while (!q.empty()&&a[q.top()]<=a[i])q.pop(); if (!q.empty()&&i-q.top()<n)l[i]=q.top(); q.push(i); } while (!q.empty())q.pop(); for (int i=2*n;i>=1;i--){ while (!q.empty()&&a[q.top()]>=a[i])q.pop(); if (!q.empty()&&q.top()-i<n)r[i]=q.top(); q.push(i); } for (int i=1;i<=2*n;++i){ if(l[i]!=-1&&r[i]!=-1){ make(r[i]-n+1,l[i]); } } vector<int> v; ll res=0; for (int i=1;i<=n;++i){ res+=d[i]; if (!res) v.push_back((n-i+1)%n); } sort(v.begin(),v.end()); cout<<v.size()<<"\n"; for(int i=0;i<v.size();++i){ cout<<v[i]<<" "; } cout<<"\n"; } int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0); cin>>T; while (T--) solve(); return 0; }
- 1
信息
- ID
- 12478
- 时间
- 2000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者