【题解】有趣的家庭菜园 / たのしい家庭菜園

文章同步发表于 CSDN洛谷Hexo

【题解】有趣的家庭菜园 / たのしい家庭菜園

双倍经验 三倍经验

Description

给定 h1n,求最少交换几次可以使得原序列 满足 h1hi1hi+1n 中同时存在某个 hi 的数。

Solution

总感觉这题就思维难度与实现难度而言评不到紫,差不多蓝的亚子

看到交换等关键词就知道与逆序对有关,这里提供一个形象化的思路。

先思考如下特殊情况:h 中没有重复元素。

假设 hxh 中的最小值。

那么,hx 不管在中间那个位置,两边都会有比它大的。

所以 hx 应该挪到序列的两端。具体是哪一端呢?移到不同的地方会不会对其他数带来不同的影响呢?

这就要根据 x1n 的距离来判断了,因为不管 hx 移到哪一端,中间剩下的数排列顺序都是一样的,所以 hx 对中间的那些数造成的影响是一样的,自然是移到距离更近的一端更优。

我们把这个操作看做将 hxh 中删去,那么剩下的数又构成了一个新的问题。重复这个操作即可得到答案。

但我们不可能模拟这个过程计算。

所以我们有必要搞明白如下这件事:

假设进行到某一步时,当前的最小值为 hy,那么就应该这么移动:

图示

所以 hy 往两边跳的过程,其实一共跳过的是 hy 左边或右边 >hy 的数的个数。

不过,这只是 h 中元素不重复的情况,有重复的情况呢,其实同理。

如果按从左到右的顺序处理序列,当 hy 向左跳时,左边未归位的数中一定没有与 hy 相等的值。(因为从左向右处理,在处理 hy 之前已经把前面与 hy 相等的值挪好了,所以不用担心左边)

但是,如果 hy 要向右跳呢?右边可能有相等的数,所以要跳过的是 hy 的数。

当然,如果你喜欢,也可以从右到左,那么此时 hy 向左就要跳过 hy 的数,向右跳过 >hy 的所有数。

那,这题用树状数组维护一下不就出来了吗?

但是,我们考虑一种情况,以下是这种情况的最简形式:

5
2 2 1 1 2

按照我们刚刚的步骤,这一组数据应该像这样处理:

2(1) 2(2) 1(3) 1(4) 2(5)

hy=1(3)

1(3) 向右挪至 2(5) 的位置,经历 2 次交换操作,此时总交换次数 ans=2

2(1) 2(2) 1(4) 2(5) 1(3)

hy=1(4)

1(4) 向右挪至 2(5) 的位置,经历 1 次交换操作,此时总交换次数 ans=3

2(1) 2(2) 2(5) 1(4) 1(3)

操作完成,最终答案 ans=3

但相信我们大家都能看出来,正确的交换次序是:

2(1) 2(2) 1(3) 1(4) 2(5)

hy=1(4)

1(4) 向右挪至 2(5) 的位置,经历 1 次交换操作,此时总交换次数 ans=1

2(1) 2(2) 1(3) 2(5) 1(4)

hy=1(3)

1(3) 向右挪至 2(5) 的位置,经历 1 次交换操作,此时总交换次数 ans=2

2(1) 2(2) 2(5) 1(3) 1(4)

操作完成,最终答案 ans=2

咦?那么不能从左到右,只能从右到左咯?

当然不行,把刚刚的数据反过来输就可以 Hack 这种做法(

这说明了我们只能适当地在不同时机选择不同的选择次序,但是我们不难发现,最终都是要把位置更靠外的数往更外面的地方挪,比如上例中的 1(4) 就比 1(3) 更靠外(右),处理次序比 1(3) 更优先, 最终的位置也比 1(3) 更靠外(右)。

而上面那条简单的规律,就帮我们免去了决定选择次序的痛苦。

因为按以上的规律,越靠外的数,处理次序更优先,所以当我们处理一个数 hy 时,hy 一定是当前序列中值最小的数中位置最靠外的,所以直接查询 >hy 的数的个数,就是 hy 要交换的次数。

Code

树状数组和离散化相信各位都会吧。在这里我们用两个树状数组当做桶,分别维护当前 hy 左边和右边的所有值的个数。

不过有个坑点在于必须输出文末回车,不然会 WA。

#include<cstdio>
#include<algorithm>
#define int long long
const int maxn=3e5+5;
const int LEN=(1<<20);
int n,a[maxn];
int ans1,ans2,ans;
int Bit[2][maxn],Lsh[maxn];
inline int Lowbit(int x){return x & (-x);}
inline int max(int x,int y){return x>y?x:y;}
inline int min(int x,int y){return x<y?x:y;}
inline void Update(int k,int x,int typ){
	for(int i=k;i<=n;i+=Lowbit(i))
		Bit[typ][i]+=x; 
	return; 
}
inline int Sum(int k,int typ){
	int ans=0;
	for(int i=k;i;i-=Lowbit(i))
		ans+=Bit[typ][i];
	return ans;
}
inline int nec(){
    static char buf[LEN],*p=buf,*e=buf;
    if(p==e){
        if((e=buf+fread(buf,1,LEN,stdin))==buf)return EOF;
        p=buf;
    }
    return *p++;
}
inline bool read(int&x){
    static char neg=0,c=nec();
    neg=0,x=0;
    while((c<'0'||c>'9')&&c!='-'){
        if(c==EOF)return 0;
        c=nec();
    }
    if(c=='-'){
        neg=1;
        c=nec();
    }
    do{x=x*10+c-'0';}while((c=nec())>='0');
    if(neg)x=-x;
    return 1;
}
signed main(){
	read(n);
	for(int i=1;i<=n;++i){
		read(a[i]);
		Lsh[i]=a[i];
	}
	std::sort(Lsh+1,Lsh+1+n);
	int cnt=std::unique(Lsh+1,Lsh+1+n)-Lsh-1;
	for(int i=1;i<=n;++i){
		a[i]=std::lower_bound(Lsh+1,Lsh+cnt+1,a[i])-Lsh;
		Update(a[i],1,1);
	}
	for(int i=1;i<=n;++i){
		Update(a[i],-1,1);
		ans1=n-i-Sum(a[i],1);
        Update(a[i],1,0);
        ans2=i-Sum(a[i],0);
		ans+=min(ans1,ans2);
	}
	printf("%lld\n",ans);
	return 0;
}

end.

posted @   XSC062  阅读(276)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示