题目描述
27%
转换一下题目,就变成
给出一个坐标系上的一个点(x,y),其中每列的权值相同,每次可以向上或向左上走一步,求走到最顶端时经过的最小的权值和
结论
所以有一个结论:
最优解法一定是先向左上走若干步(或不走),之后直接走到顶
证明
可以感性证明一下:
假设这是一个解
如果有更优的解,那么应该是这样的(其它情况类似)
该解更优,也就是说灰色部分的和<蓝色部分的和
由题目可得,蓝色部分的权值全部相等
所以灰色部分中一定有至少一个数<蓝色部分(不然权值肯定大于蓝色部分)
假设绿色点就是小于蓝色的权值(如果大于的话可以往右移,这样的解一定会更优)
那么这样走一定比先前的两种走法都更优
所以最优的走法还是先向左上走若干步(或不走),之后直接走到顶
也就是说,只要有不是这样走的解,都一定能找到一种更优的解
所以直接 O(nq) O ( n q ) 枚举就行了
42%
考虑离线。
设从一个点(x,i)走到第j列(j<=i,x是自变量)
可以构出一个一次函数,表示花费的代价
通过观察发现,因为j有n种取值,所以y=kx+b中的k也有n种取值
再推一波式子可以把整个函数求出来
以我自己的式子为例(可能有不同的推法):
设当前处理到询问
(X,Y)
(
X
,
Y
)
,第i个询问求得的自变量为x[i]
x[i]=X−(Y−d[i]+1)
x
[
i
]
=
X
−
(
Y
−
d
[
i
]
+
1
)
x[i]=X−Y+(d[i]−1)
x
[
i
]
=
X
−
Y
+
(
d
[
i
]
−
1
)
因为y=kx+b,所以可以把
(d[i]−1)∗k[i]
(
d
[
i
]
−
1
)
∗
k
[
i
]
当作
b[i]
b
[
i
]
把询问按y从小到大排序,依次加入每条直线,维护一下斜率单调递增,然后直接暴力找
因为数据随机,所以
O(
O
(
玄
)
)
~能过
)
)
<script id="MathJax-Element-10" type="math/tex">)</script>
关于斜率单调递增的证明
推式子是不可能推式子的,这辈子都不可能推式子的
证明又不会证,只有靠口胡来维持生活
咳咳
如果斜率不是单调递增,那么可以有
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;
}