P3253 [JLOI2013]删除物品
思路解析
主要难点就在于两个堆之间的变化。
当得出此题能够抽象化为
将数字输入顺序从 $1 \sim n1+n2 $ 进行编号。得到一个序列,组成如下:
-
前 \(n1\) 位由编号为 \(1 \sim n1\) 的数字逆序填。
-
第 \(n1+1 \sim n1+n2\) 位由数字编号为 $n1+1 \sim n1+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\)