[bzoj5380][单调栈]Function

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Rose_max/article/details/82250809

题目传送门

题解

就只会天天划水啦…
有一个我发现不了的性质..
一个点(x,y)的答案,一定是它向左上走若干步,然后一直向上走所获得的总贡献
写成柿子,设走到纵坐标为x的位置获得的贡献

S y S z + a z ( x y + z )

其中S表示a的前缀和
拆开化简有
a z ( x y ) + a z z S z + S y

由于 S y 获得的贡献固定所以只需要维护
a z ( x y ) + a z z + S z

维护一些直线 y = k x + b ,其中 k = a z b = a z z + S z
要找到 x y 在这些直线里的最小取值即可
维护一个上凸壳,但是斜率不单调怎么办
又出来一个性质,如果 i < j a i > a j ,那么 a i 永远不会做出贡献
这样可以让斜率单调递增
维护一个单调栈与单调栈里相邻两条直线的交点,画图可知这些交点的x值单调递增
于是单调栈里的交点x值随栈的增大而减小
二分答案即可

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<vector>
#define eps 1e-5
#define LL long long
#define mp(x,y) make_pair(x,y)
using namespace std;
vector< pair<int,int> > q[500005];
struct stack
{
    int k,id;LL b;
    stack(){}
    stack(int _k,int _id,LL _b){k=_k;id=_id;b=_b;}
}sta[500005];int tp;
int a[500005],n,m;LL s[500005];
inline int read()
{
    int f=1,x=0;char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f; 
}
double tmp[500005];
int fd(int x)
{
    int l=1,r=tp-1,ret=0;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(tmp[mid]-(double)x>=eps)ret=mid,l=mid+1;
        else r=mid-1;
    }
    return ret+1;
}
LL b[500005];
bool chk(int l1,int l2,int l3)
{
    double x=(double)(b[l3]-b[l1])/(a[l1]-a[l3]);
    double g1=(double)x*a[l1]+b[l1];
    double g2=(double)x*a[l2]+b[l2];
    if(g2-g1>=eps)return true;
    return false;
}
LL ans[500005];
int main()
{
//  freopen("a.in","r",stdin);
//  freopen("a.out","w",stdout);
    n=read();
    for(int i=1;i<=n;i++)a[i]=read(),s[i]=s[i-1]+(LL)a[i],b[i]=(LL)a[i]*i-s[i];
    m=read();
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        q[y].push_back(mp(x,i));
    }
    for(int i=1;i<=n;i++)
    {
        while(tp&&sta[tp].k>=a[i])tp--;
        while(tp>1&&chk(sta[tp-1].id,sta[tp].id,i))tp--;
        sta[++tp]=stack(a[i],i,(LL)a[i]*i-s[i]);
        if(tp>1)tmp[tp-1]=(double)(b[sta[tp-1].id]-b[sta[tp].id])/(a[sta[tp].id]-a[sta[tp-1].id]);
        if(q[i].size())
            for(int j=0;j<q[i].size();j++)
            {
                int x=q[i][j].first,id=q[i][j].second;
                int pos=fd(x-i);
                ans[id]=(LL)sta[pos].k*(x-i)+(LL)a[sta[pos].id]*sta[pos].id+s[i]-s[sta[pos].id];
                /*for(int k=1;k<=tp;k++)
                {
                    int pos=k;
                    ans[id]=min(ans[id],(LL)sta[pos].k*(x-i)+a[pos]*pos+s[i]-s[pos]);
                }
                printf("CHECKER: %d\n",i);
                for(int i=1;i<=tp;i++)printf("%d %d    ",sta[i].id,sta[i].b);
                puts("");*/
            }
    }
    for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Rose_max/article/details/82250809