【题解】有趣的家庭菜园 / たのしい家庭菜園
【题解】有趣的家庭菜园 / たのしい家庭菜園
Description
给定 ,求最少交换几次可以使得原序列 不 满足 和 中同时存在某个 的数。
Solution
总感觉这题就思维难度与实现难度而言评不到紫,差不多蓝的亚子
看到交换等关键词就知道与逆序对有关,这里提供一个形象化的思路。
先思考如下特殊情况: 中没有重复元素。
假设 是 中的最小值。
那么, 不管在中间那个位置,两边都会有比它大的。
所以 应该挪到序列的两端。具体是哪一端呢?移到不同的地方会不会对其他数带来不同的影响呢?
这就要根据 离 和 的距离来判断了,因为不管 移到哪一端,中间剩下的数排列顺序都是一样的,所以 对中间的那些数造成的影响是一样的,自然是移到距离更近的一端更优。
我们把这个操作看做将 从 中删去,那么剩下的数又构成了一个新的问题。重复这个操作即可得到答案。
但我们不可能模拟这个过程计算。
所以我们有必要搞明白如下这件事:
假设进行到某一步时,当前的最小值为 ,那么就应该这么移动:
所以 往两边跳的过程,其实一共跳过的是 左边或右边 的数的个数。
不过,这只是 中元素不重复的情况,有重复的情况呢,其实同理。
如果按从左到右的顺序处理序列,当 向左跳时,左边未归位的数中一定没有与 相等的值。(因为从左向右处理,在处理 之前已经把前面与 相等的值挪好了,所以不用担心左边)
但是,如果 要向右跳呢?右边可能有相等的数,所以要跳过的是 的数。
当然,如果你喜欢,也可以从右到左,那么此时 向左就要跳过 的数,向右跳过 的所有数。
那,这题用树状数组维护一下不就出来了吗?
但是,我们考虑一种情况,以下是这种情况的最简形式:
5
2 2 1 1 2
按照我们刚刚的步骤,这一组数据应该像这样处理:
向右挪至 的位置,经历 次交换操作,此时总交换次数 。
向右挪至 的位置,经历 次交换操作,此时总交换次数 。
操作完成,最终答案 。
但相信我们大家都能看出来,正确的交换次序是:
向右挪至 的位置,经历 次交换操作,此时总交换次数 。
向右挪至 的位置,经历 次交换操作,此时总交换次数 。
操作完成,最终答案 。
咦?那么不能从左到右,只能从右到左咯?
当然不行,把刚刚的数据反过来输就可以 Hack 这种做法(
这说明了我们只能适当地在不同时机选择不同的选择次序,但是我们不难发现,最终都是要把位置更靠外的数往更外面的地方挪,比如上例中的 就比 更靠外(右),处理次序比 更优先, 最终的位置也比 更靠外(右)。
而上面那条简单的规律,就帮我们免去了决定选择次序的痛苦。
因为按以上的规律,越靠外的数,处理次序更优先,所以当我们处理一个数 时, 一定是当前序列中值最小的数中位置最靠外的,所以直接查询 的数的个数,就是 要交换的次数。
Code
树状数组和离散化相信各位都会吧。在这里我们用两个树状数组当做桶,分别维护当前 左边和右边的所有值的个数。
不过有个坑点在于必须输出文末回车,不然会 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.
—— · EOF · ——
真的什么也不剩啦 😖
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 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】