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;
}

 

posted @ 2017-12-06 13:41  Star_Feel  阅读(182)  评论(0编辑  收藏  举报