专栏文章
题解:AT_abc396_f [ABC396F] Rotated Inversions
AT_abc396_f题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mipxfc2o
- 此快照首次捕获于
- 2025/12/03 19:31 3 个月前
- 此快照最后确认于
- 2025/12/03 19:31 3 个月前
题目翻译
给定整数 , 和一个长度为 的非负整数序列 。对于每个 ,计算序列 的逆序数。其中 。
逆序数的定义是满足 且 的整数对数目。
问题分析
我们需要计算每个 对应的逆序数。直接对每个 生成序列 并计算逆序数的时间复杂度是 ,这显然不可行。我们需要预处理和数学规律来快速计算每个 的结果。
关键思路
-
预处理贡献值:当 变化时,某些元素会“绕圈”(即 ),这会影响逆序数的计算。预处理每个元素绕圈时的贡献变化。
-
初始逆序数:使用归并排序计算 时的逆序数。
-
动态调整逆序数: 根据 的变化,利用预处理的值调整逆序数,避免重复计算。
步骤
-
计算初始逆序数:使用归并排序计算 时的逆序数。
-
预处理贡献值: 记录当元素 开始绕圈时,后面比它小的元素数目(减少的逆序对数)。_ 记录当元素 开始绕圈时,前面比它大的元素数目(新增的逆序对数)。
-
动态调整:对于每个 ,调整初始逆序数,减去减少的贡献值,加上新增的贡献值。
代码
CPP#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,m,ans,a[200001],b[200001],cnt[200001],sum[200001],cnt_[200001],sum_[200001];
// 归并排序计算逆序数
void merge(int l,int r){
if(l>=r)return;
int mid=(l+r)/2;
merge(l,mid);merge(mid+1,r);
int i=l,j=mid+1,cnt=l;
while(i<=mid && j<=r){
if(a[i]<=a[j])b[cnt++]=a[i++];
else{
b[cnt++]=a[j++];
ans+=mid-i+1; // 统计逆序对
}
}
while(i<=mid)b[cnt++]=a[i++];
while(j<=r)b[cnt++]=a[j++];
for(int k=l;k<=r;k++)a[k]=b[k];
}
signed main(){
cin >> n >> m;
for(int i=1;i<=n;i++)cin >> a[i];
// 预处理 sum 数组:从后往前统计每个元素绕圈后的贡献
for(int i=n;i>=1;i--){
cnt[a[i]]++; // 记录当前元素出现次数
sum[a[i]]+=(n-i+1-cnt[a[i]]); // 后面非 a[i] 的元素数目
}
// 预处理 sum_ 数组:从前往后统计每个元素绕圈后的贡献
for(int i=1;i<=n;i++){
cnt_[a[i]]++;
sum_[a[i]]+=(i-cnt_[a[i]]); // 前面非 a[i] 的元素数目
}
// 计算初始逆序数(k=0 的情况)
merge(1,n);
cout << ans << "\n";
// 处理每个 k 从 1 到 M-1
for(int k=1;k<m;k++){
ans=ans-sum[m-k]+sum_[m-k];
cout << ans << "\n";
}
return 0;
}
代码注释
-
归并排序:用于计算初始逆序数( 时的结果)。
-
预处理 和 _: 统计当元素 绕圈时,后面比它小的元素数目。_ 统计当元素 绕圈时,后面比它大的元素数目。
-
动态调整:对于每个 ,调整逆序数,利用预处理的值快速计算。
时间复杂度
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...