BZOJ3192: [JLOI2013]删除物品
【传送门:BZOJ3192】
简要题意:
给出两个堆,两个堆里的所有数保证没有相同的,可以将其中一个堆的堆顶移到另一个堆的堆顶,花费为1,但是如果当前要移动的数为两个堆的最大值,则不需要花费,并且将这个数删除,求出将所有数删除的最小花费
题解:
其实可以将两个堆合并成一个序列,其中第一个堆要倒着来,这样子只要确定断点就可以确定两个堆的堆顶了,而且只要断点移动就相当于移动堆顶
首先将所有数排序,得到删除的顺序,那么我们可以将每个数的权值设为1,那么只要将每次断点到要删除的数的距离累积就可以求出答案,但是如果要删除的话,就要这个数的权值变为0,但是暴力肯定不行
所以我们用树状数组维护前缀和
注意:答案可能会爆int,要加long long
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; int s[110000]; struct node { int x,d,s; }l[110000]; typedef long long LL; int a[110000]; int cmp(const void *xx,const void *yy) { node n1=*(node *)xx; node n2=*(node *)yy; if(n1.d>n2.d) return -1; if(n1.d<n2.d) return 1; return 0; } int mx[110000]; int lowbit(int x){return x&-x;} int n; void change(int x,int d) { while(x<=n) { a[x]+=d; x+=lowbit(x); } } int getsum(int x) { int ans=0; while(x!=0) { ans+=a[x]; x-=lowbit(x); } return ans; } int main() { int n1,n2; scanf("%d%d",&n1,&n2); for(int i=n1;i>=1;i--) scanf("%d",&s[i]); for(int i=n1+1;i<=n1+n2;i++) scanf("%d",&s[i]); n=n1+n2; for(int i=1;i<=n;i++) l[i].x=i,l[i].d=s[i]; qsort(l+1,n,sizeof(node),cmp); for(int i=1;i<=n;i++) change(i,1); int mid=n1; LL ans=0; for(int i=1;i<=n;i++) { if(l[i].x>mid) ans+=getsum(l[i].x-1)-getsum(mid); else ans+=getsum(mid)-getsum(l[i].x); change(l[i].x,-1); mid=l[i].x; } printf("%lld\n",ans); return 0; }
渺渺时空,茫茫人海,与君相遇,幸甚幸甚