P3253 [JLOI2013]删除物品

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\)

posted @ 2023-01-31 23:54  k_stefani  阅读(20)  评论(0编辑  收藏  举报