首页 > 其他 > 详细

【BZOJ3897】Power 线段树求前驱后缀

时间:2015-03-25 17:22:50      阅读:179      评论:0      收藏:0      [点我收藏+]

链接:

#include <stdio.h>
int main()
{
    puts("转载请注明出处[vmurder]谢谢");
    puts("网址:blog.csdn.net/vmurder/article/details/44622633");
}

题解:

首先我们从大到小排个序,然后感性来讲,肯定是从大到小能取多少就取多少的。然后我们取一个点时,还不能取多了,不符题意或者影响到其它已经取了的点对吧。

所以我们求个前驱pre,后缀suc,就是最近的前面的取了的点,和后面的。
这个可以用线段树求,也可以set神马的快速水过啦啦啦。

然后此点既然是之前取的,肯定是能取多少尽量取多少啦,拿后缀举例,也就是你取当前点,然后等到后缀那个点,肯定需要恢复完全才可以,不能当前点取太多影响到后缀那个点取了的数量对吧。

我们研究此点、前驱、后继之间的关系,以此来确定此点能取多少。
首先到每个点时次数不能大于limit,这是第一个限制
首先前驱那里一定是取完了,所以到这个点时至多回复了两点距离天数*recover/day。
然后后继那里同理,也是距离*recover

然后前驱和后继之间肯定是不互相影响或者说影响过了的。
那么其中最多插入前驱后继距离*recover-limit的次数。

long long temp=lim;
if(pre>=1)temp=min(temp,(long long)(src[i].id-pre)*rec);
if(suc<=n)temp=min(temp,(long long)(suc-src[i].id)*rec);
if(pre>=1&&suc<=n)temp=min(temp,(long long)(suc-pre)*rec-lim);
if(temp>0)
{
    ans+=(long long)src[i].x*temp;
    add(src[i].id);
}

然后如果取的次数负了的话肯定是不取了对吧。

代码:

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 501000
#define inf 0x3f3f3f3f
#define ls (note<<1)
#define rs (note<<1|1)
using namespace std;
int n,lim,rec,a[N];
long long ans;
struct Segment_Tree
{
    int x,l,r;
}s[N<<2];
int crs[N];
void build(int note,int l,int r)
{
    s[note].x=0;
    s[note].l=l,s[note].r=r;
    if(l==r)
    {
        crs[l]=note;
        return ;
    }
    int mid=l+r>>1;
    build(ls,l,mid),build(rs,mid+1,r);
}
inline void add(int x)
{
    x=crs[x];
    while(x)s[x].x++,x>>=1;
}
inline int pred(int note,int x) // [1,x]这段中最后一个有值的。
{
    if(!s[note].x)return 0;
    if(s[note].l==s[note].r)return s[note].l;
    int mid=s[note].l+s[note].r>>1;
    if(x>mid)
    {
        int ret=pred(rs,x);
        if(ret)return ret;
        else return pred(ls,s[ls].r);
    }
    else return pred(ls,x);
}
inline int succ(int note,int x) // [x,n]这段中最后一个有值的。
{
    if(!s[note].x)return n+1;
    if(s[note].l==s[note].r)return s[note].l;
    int mid=s[note].l+s[note].r>>1;
    if(x<=mid)
    {
        int ret=succ(ls,x);
        if(ret<=n)return ret;
        else return succ(rs,s[rs].l);
    }
    else return succ(rs,x);
}
struct ELI
{
    int x,id;
    bool operator < (const ELI &A)const
    {return x<A.x;}
}src[N];
void work3()
{
    ans=0;
    int i,j,k;
    for(i=1;i<=n;i++)
    {
        src[i].x=a[i];
        src[i].id=i;
    }
    sort(src+1,src+n+1);
    build(1,1,n);
    for(i=n;i;i--)
    {
        int pre=pred(1,src[i].id);
        int suc=succ(1,src[i].id);
        long long temp=lim;
        if(pre>=1)temp=min(temp,(long long)(src[i].id-pre)*rec);
        if(suc<=n)temp=min(temp,(long long)(suc-src[i].id)*rec);
        if(pre>=1&&suc<=n)temp=min(temp,(long long)(suc-pre)*rec-lim);
        if(temp>0)
        {
            ans+=(long long)src[i].x*temp;
            add(src[i].id);
        }
    }
    cout<<ans<<endl;
    return ;
}
int main()
{
    freopen("test.in","r",stdin);
//  freopen("arbeit.out","w",stdout);

    int i,j,k,g;
    for(scanf("%d",&g);g--;)
    {
        scanf("%d%d%d",&lim,&rec,&n);
        for(i=1;i<=n;i++)scanf("%d",&a[i]);
        work3();
    }
    return 0;
}

【BZOJ3897】Power 线段树求前驱后缀

原文:http://blog.csdn.net/vmurder/article/details/44622633

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!