P3253 [JLOI2013]删除物品

P3253 [JLOI2013]删除物品

思路解析

主要难点就在于两个堆之间的变化

当得出此题能够抽象化为

将数字输入顺序从 1n1+n2 进行编号。得到一个序列,组成如下:

  • n1 位由编号为 1n1 的数字逆序填。

  • n1+1n1+n2 位由数字编号为 n1+1n1+n2 的数字顺序填。

堆之间的变化实际上是堆顶的变化,只需要记录堆顶的位置,以及被取走的数,即可表示出两个堆的状态。

top 为第一个堆的堆顶所在的位置。每次更新时:

  • 当取出的是第一个堆的数,堆顶即为这个数位置在左边的那个数,1 即可。

  • 取出的是第二个堆的数,堆顶也为这个数左边的数,1即可。

对于答案 ans 的计算:

  • 当取出的是第一个堆的数,移走的数为第一堆堆顶到该数之间的数,即 ans+=sum(top)sum(a[i].pos)

  • 当取出的是第二个堆的数,移走的数为第二堆堆顶到该数之间的数,此时第二堆的堆顶不能直接用 top+1 表示,因为不知道其下一个位置的数是否被取走,因此直接默认多算一个,再减去即可。ans+=sum(a[i].pos)sum(top)1

code

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
struct A{
int pos,num;
}a[100005];
int n1,n2,c[100005],top;
long long ans;
bool cmp(A x,A y){ return x.num>y.num;}
void add(int x,int y){
for(;x<=n1+n2;x+=(x&-x)) c[x]+=y;
}
int sum(int x){
int k=0;
for(;x;x-=(x&-x)) k+=c[x];
return k;
}
int main(){
scanf("%d%d",&n1,&n2);
top=n1;
for(int i=n1;i>=1;i--) scanf("%d",&a[i].num),a[i].pos=i,add(i,1);
for(int i=n1+1;i<=n1+n2;i++) scanf("%d",&a[i].num),a[i].pos=i,add(i,1);
sort(a+1,a+n2+n1+1,cmp);
top=n1;
for(int i=1;i<=n1+n2;i++){
if(a[i].pos<=top) ans+=sum(top)-sum(a[i].pos);
else ans+=sum(a[i].pos)-sum(top)-1;
top=a[i].pos-1;
add(a[i].pos,-1);
}
cout<<ans<<endl;
return 0;
}

2023.1.31

posted @   k_stefani  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示