jzoj5784. 【省选模拟2018.8.8】 Function

版权声明:233 https://blog.csdn.net/gmh77/article/details/81537545

题目描述

这里写图片描述

27%

转换一下题目,就变成
给出一个坐标系上的一个点(x,y),其中每列的权值相同,每次可以向上或向左上走一步,求走到最顶端时经过的最小的权值和

结论

所以有一个结论:
最优解法一定是先向左上走若干步(或不走),之后直接走到顶

证明

可以感性证明一下:
这里写图片描述
假设这是一个解

这里写图片描述
如果有更优的解,那么应该是这样的(其它情况类似)
该解更优,也就是说灰色部分的和<蓝色部分的和

由题目可得,蓝色部分的权值全部相等
所以灰色部分中一定有至少一个数<蓝色部分(不然权值肯定大于蓝色部分)

这里写图片描述
假设绿色点就是小于蓝色的权值(如果大于的话可以往右移,这样的解一定会更优)

这里写图片描述
那么这样走一定比先前的两种走法都更优
所以最优的走法还是先向左上走若干步(或不走),之后直接走到顶

也就是说,只要有不是这样走的解,都一定能找到一种更优的解


所以直接 O ( n q ) 枚举就行了

42%

考虑离线。

设从一个点(x,i)走到第j列(j<=i,x是自变量)
可以构出一个一次函数,表示花费的代价

通过观察发现,因为j有n种取值,所以y=kx+b中的k也有n种取值
再推一波式子可以把整个函数求出来


以我自己的式子为例(可能有不同的推法):
设当前处理到询问 ( X , Y ) ,第i个询问求得的自变量为x[i]
x [ i ] = X ( Y d [ i ] + 1 )
x [ i ] = X Y + ( d [ i ] 1 )
因为y=kx+b,所以可以把 ( d [ i ] 1 ) k [ i ] 当作 b [ i ]


把询问按y从小到大排序,依次加入每条直线,维护一下斜率单调递增,然后直接暴力找
因为数据随机,所以 O ( ) ~ O ( 能过 )

关于斜率单调递增的证明

推式子是不可能推式子的,这辈子都不可能推式子的
证明又不会证,只有靠口胡来维持生活

咳咳
如果斜率不是单调递增,那么可以有
a[i]>a[j] (i< j)
(等于的情况也差不多)

问题是如何证明这不是最优的解
这里写图片描述
如果a[i]>a[j],显然上面的蓝色段肯定大于红色段
这里写图片描述
所以下面的黄色段肯定小于绿色段
因为红绿段的单位权值相同,而黄绿段的个数相同且黄段<绿段
所以黄段中一定有至少一个数小于红/绿段的单位权值(不然不可能小于)

设其位置为k
这里写图片描述
则a[i]>a[j]>a[k]
接着可以发现这样并不是最优
这里写图片描述
如图,可以将下面的黄段移到上面,结果不变,但a[i]>a[k],所以总结果会更优

但是这样并不是最优解,根据27%的结论,i~k中间一定有一个数a[l],
这里写图片描述
使得这样才是最优解
根据上面的结论,可以得出
a[i]>a[j]>a[k]>a[l]
于是a[l]< a[i]


所以
当i< j时,若a[i]≥a[j],则一定有一个更优的解k,并且a[k]< a[j]
所以最优解一定单调递增

100%

因为题目所要求的是一个最小值,所以直接维护一个下凸壳,之后再凸壳上二分
(当然前提是斜率单调递增)

code

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
using namespace std;

long long a[500001];
struct A{
    int x,y,id;
} b[500001];
int d[500001];
long long B[500001];
long long b2[500001];
long long ans[500001];
int n,q,I,i,j,k,l,r,mid,t;
long long s;

bool cmp(A a,A b)
{
    return a.y<b.y;
}

long long js(int t,int x) {return a[t]*x+b2[t];}
double jd(int x,int y) {return (double)(b2[x]-b2[y])/(a[y]-a[x]);}

int main()
{
    freopen("function.in","r",stdin);
    freopen("function.out","w",stdout);

    scanf("%d",&n);
    fo(i,1,n)
    scanf("%lld",&a[i]);
    scanf("%d",&q);
    fo(i,1,q)
    scanf("%d%d",&b[i].x,&b[i].y),b[i].id=i;

    sort(b+1,b+q+1,cmp);

    t=0;
    I=1;
    fo(i,1,n)
    {
        s+=a[i];
        B[i]=B[i-1]-a[i-1];
        b2[i]=B[i]+a[i]*(i-1);

        while (t && a[d[t]]>=a[i])
        t--;
        while (t>1 && jd(d[t-1],i)<jd(d[t],i))//维护凸壳
        t--;
        d[++t]=i;

        while (I<=q && b[I].y==i)
        {
            ans[b[I].id]=9223372036854775807;

            l=1;
            r=t;
            while (l<r)
            {
                mid=(l+r)/2;

                if (d[mid]<(b[I].y-b[I].x+1))
                l=mid+1;
                else
                r=mid;
            }

            r=t-1;
            while (l<r)
            {
                mid=(l+r)/2;

                if (js(d[mid],b[I].x-b[I].y)>js(d[mid+1],b[I].x-b[I].y))
                l=mid+1;
                else
                r=mid;
            }

            while (l<t && js(d[l],b[I].x-b[I].y)>=js(d[l+1],b[I].x-b[I].y))
            l++;

            ans[b[I].id]=js(d[l],b[I].x-b[I].y)+s;
            I++;
        }
    }

    fo(i,1,q)
    printf("%lld\n",ans[i]);

    fclose(stdin);
    fclose(stdout);

    return 0;
}

猜你喜欢

转载自blog.csdn.net/gmh77/article/details/81537545