2 条题解
-
1
P10843 【MX-J2-T4】Turtle and Cycles 题解
解题思路
首先,观察操作的本质。设 , , ,那么操作将 变为 。考虑差分数组,定义 (环形处理,)。操作前,, ;操作后,, 。因此,一次操作相当于交换了相邻的差分值 和 。
一个位置 是好的当且仅当 且 。在差分数组上,这等价于 且 。定义二进制序列 如果 ,否则 。那么好位置对应 且 的位置。
序列是好的当且仅当恰好有一个好位置,即在环形序列中恰好有一个 且 的位置。这在环形二进制序列中意味着所有 是连续的,所有 是连续的(即序列形如 或 ,但根据差分条件,需要的是 到 的变化点)。
通过操作交换相邻差分值,我们可以重新排列差分数组。问题转化为:通过相邻交换,将二进制序列 排列成所有 连续的形式(即连续 后接连续 ),所需的最小交换次数。
最小交换次数的计算等价于将所有的 移动到连续位置所需的最小移动步数。设 的个数为 , 的位置为 (排序后)。目标是将这些 移动到连续位置 。最小交换次数为 。
在环形序列中,需要处理环的循环特性。一个常见技巧是将序列复制一份,然后计算线性序列上的最小代价。
算法实现
根据排列 计算差分值 ,并生成二进制序列 。
将 复制一份得到长度为 的序列,以便处理环形。
设 表示 中 的个数。
表示 中 的索引加权和(即 )。
表示 中 的逆序索引加权和(逆序索引为 )。
计算最小代价:对于每个可能的起始位置 (),将环形序列视为从 开始的长度为 的段,计算将 移动到该段前半部分和后半部分的代价:
左半部分 代价:
右半部分 代价:$rres = sg[mid+1] - sg[i+n] - rk * (2*n - (i+n-1) + 1) - (rk-1)*rk/2$
总代价为 ,取最小值。
复杂度分析 时间复杂度:
空间复杂度:
Code
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=4e5+5; int t, n, a[N]; ll g[N],f[N],sg[N]; bool b[N]; ll cal(int n){ ll ans=1e18; for(int i=1; i<=n; i++) { int mid=i+n/2-1; ll lk=f[mid]-f[i-1]; ll lres=g[mid]-g[i-1]-lk*i-(lk-1)*lk/2; ll rk=f[i+n-1]-f[mid]; ll rres=sg[mid+1]-sg[i+n]-rk*(2*n-(i+n-1)+1)-(rk-1)*rk/2; ans = min(ans,lres+rres); } return ans; } void solve() { cin>>n; for(int i=1; i<=n; i++) { cin>>a[i]; a[i+n]=a[i]; } for(int i=2; i<=n+1; i++) { b[i-1]=(a[i]>a[i-1]); b[i-1+n]=b[i-1]; } int tot = 2*n; for(int i=1; i<=tot; i++) { f[i]=f[i-1]+b[i]; g[i]=g[i-1]+i*b[i]; } sg[tot+1]=0; for(int i=tot,j=1; i>=1; i--,j++) { sg[i]=sg[i+1]+j*b[i]; } cout<<cal(n)<<'\n'; } int main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin>>t; while(t--) solve(); return 0; }(这是本蒟蒻的第一篇题解,不喜勿喷,QAQ)
- 1
信息
- ID
- 24
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 6
- 标签
- 递交数
- 478
- 已通过
- 28
- 上传者