「HDOJ 1394」Minimum Inversion Number 题解
本文网址:https://www.cnblogs.com/zsc985246/p/16366573.html ,转载请注明出处。
题目大意
给你一个 个数的序列,其中只有 ~ ,可以把第一个数移到最后一个,次数不限。
求在所有能够生成的数列中逆序对的最小数量。
思路
暴力的想法肯定是求 次逆序对。
实际上,我们将 放到 , ~ 是没有改变的。我们可以尝试找到两个数列逆序对数目的关系。我们可以分成两部分考虑。
-
当 数列去掉 时,逆序对个数会少数列中比 小的数的个数。由于数列只有 ~ ,所以比 小的数的个数就等于 。
-
当 数列增加 时,逆序对个数会多比 大的数的个数。由于数列只有 ~ ,所以比 小的数的个数就等于 。
所以: 。
代码
归并排序
#include<bits/stdc++.h>
#define ll long long//这回是long long了
const ll N=100010;
using namespace std;
ll n;//长度
ll a[N];//原数组
ll aa[N];//排序数组
ll t[N];//合并数组
ll cnt;//逆序对数目
ll ans;//最小逆序对数目
void merge(ll l1,ll r1,ll l2,ll r2){//合并
ll i=l1;//遍历第一个序列
ll j=l2;//遍历第二个序列
ll k=l1;//遍历合并序列
while(i<=r1&&j<=r2){//两个序列都还有数
if(a[i]<=a[j]){
t[k++]=a[i++];//第一个序列的数加入合并序列
}else{
t[k++]=a[j++];//第二个序列的数加入合并序列
cnt+=r1-i+1;//统计逆序对
}
}
//第一个序列剩下的数整体放入合并序列
while(i<=r1){
t[k++]=a[i++];
}
//第二个序列剩下的数整体放入合并序列
while(j<=r2){
t[k++]=a[j++];
}
for(ll l=l1;l<=r2;l++)a[l]=t[l];
}
void merge_sort(ll l,ll r){//归并排序
if(l==r)return;//边界条件
ll mid=(l+r)>>1;
merge_sort(l,mid);//分治
merge_sort(mid+1,r);//分治
merge(l,mid,mid+1,r);//合并
}
int main(){
//读入,注意多组数据
while(scanf("%lld",&n)!=EOF){
for(ll i=1;i<=n;i++){
scanf("%lld",&a[i]);
aa[i]=a[i];
}
ans=0x3f3f3f3f,cnt=0;//初始化
merge_sort(1,n);//归并排序
for(ll i=1;i<=n;i++){
ans=min(ans,cnt);//答案
cnt+=n-2*aa[i]-1;//统计
}
printf("%lld\n",ans);//输出
}
return 0;
}
线段树
#include<bits/stdc++.h>
#define ll long long//这回是long long了
const ll N=200010;//四倍数组
using namespace std;
ll n;//长度
ll a[N];//原数组
ll sum[N];//线段树
ll cnt;//逆序对数目
ll ans;//最小逆序对数目
void change(ll rt,ll l,ll r,ll pos){//单点修改
if(l==r){//单点
sum[rt]++;//更改
return;
}
ll mid=l+r>>1;//中间值
ll lson=rt<<1,rson=rt<<1|1;//左右儿子
if(pos<=mid)change(lson,l,mid,pos);//在左子树
else change(rson,mid+1,r,pos);//在右子树
sum[rt]=sum[lson]+sum[rson];//维护
}
ll query(ll rt,ll l,ll r,ll x,ll y){//区间求和
if(x<=l&&r<=y)return sum[rt];//包含此区间
ll mid=l+r>>1;//中间值
ll tmp=0;//临时记录答案
ll lson=rt<<1,rson=rt<<1|1;//左右儿子
if(x<=mid)tmp+=query(lson,l,mid,x,y);//左儿子在区间内
if(mid<r)tmp+=query(rson,mid+1,r,x,y);//右儿子在区间内
return tmp;//返回答案
}
int main(){
//读入,注意多组数据
while(scanf("%lld",&n)!=EOF){
memset(sum,0,sizeof(sum));//初始化
ans=0x3f3f3f3f,cnt=0;//初始化
for(ll i=1;i<=n;i++){
scanf("%lld",&a[i]);
cnt+=query(1,1,n,a[i]+1,n);//先询问
change(1,1,n,a[i]);//后加入
}
for(ll i=1;i<=n;i++){
ans=min(ans,cnt);//答案
cnt+=n-2*a[i]-1;//统计
}
printf("%lld\n",ans);//输出
}
return 0;
}
树状数组
#include<bits/stdc++.h>
#define ll long long//这回是long long了
const ll N=200010;//四倍数组
using namespace std;
ll n;//长度
ll a[N];//原数组
ll sum[N];//树状数组
ll cnt;//逆序对数目
ll ans;//最小逆序对数目
void change(ll x){//单点修改
while(x<=n){
sum[x]++;//更改
x+=x&-x;//下一个节点
}
}
ll query(ll x){//区间求和
ll tmp=0;//临时存储答案
while(x){//向下寻找
tmp+=sum[x];//统计
x-=x&-x;//下一个节点
}
return tmp;//返回答案
}
int main(){
//读入,注意多组数据
while(scanf("%lld",&n)!=EOF){
memset(sum,0,sizeof(sum));//初始化
ans=0x3f3f3f3f,cnt=0;//初始化
for(ll i=1;i<=n;i++){
scanf("%lld",&a[i]);
//这里a[i]+1是为了防止0出现
//原因:0+lowbit(0)=0,在change的时候会死循环
cnt+=query(n)-query(a[i]+1);//先询问
change(a[i]+1);//后加入
}
for(ll i=1;i<=n;i++){
ans=min(ans,cnt);//答案
cnt+=n-2*a[i]-1;//统计
}
printf("%lld\n",ans);//输出
}
return 0;
}
zkw线段树
#include<bits/stdc++.h>
#define ll long long
const ll N=200010;//二倍数组
using namespace std;
ll n;//长度
ll m;//非叶子节点
ll a[N];//原数组
ll sum[N];//线段树
ll cnt;//逆序对数目
ll ans;//最小逆序对数目
void change(ll pos){//单点修改
pos+=m;//直接跳到对应节点
for(;pos;pos>>=1){//找父亲
sum[pos]++;//维护
}
}
ll query(ll s,ll t){//区间求和
ll tmp=0;//临时答案
s+=m-1,t+=m+1;//直接跳到对应节点
for(;s^t^1;s>>=1,t>>=1){
if(s&1^1)tmp+=sum[s^1];//统计
if(t&1)tmp+=sum[t^1];//统计
}
return tmp;//返回
}
int main(){
//读入,注意多组数据
while(scanf("%lld",&n)!=EOF){
memset(sum,0,sizeof(sum));//初始化
for(m=1;m<=n;m<<=1);
ans=0x3f3f3f3f,cnt=0;//初始化
for(ll i=1;i<=n;i++){
scanf("%lld",&a[i]);
cnt+=query(a[i]+1,n);//先询问
change(a[i]);//后加入
}
for(ll i=1;i<=n;i++){
ans=min(ans,cnt);//答案
cnt+=n-2*a[i]-1;//统计
}
printf("%lld\n",ans);//输出
}
return 0;
}
关于zkw线段树
尾声
如果你发现了问题,你可以直接回复这篇题解
如果你有更好的想法,也可以直接回复!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现