[jzoj]3432. 【GDOI2014模拟】服务器(斜率优化或单调队列)

来源:互联网 发布:锯棕榈 知乎 编辑:程序博客网 时间:2024/06/13 03:15

https://jzoj.net/senior/#main/show/3432

Problem

给定一个长度为N的序列A,对于第i个位置,可以直接花费Ai的费用,或者间接花费j-i(j是第j个位置,必须是直接花费的),要求在这样的条件下,序列A的每一个数都得直接花费或间接花费,求最小费用.

Data constraint

60%的数据中,1 <= n <= 1 000
100%的数据中,1 <= n <= 1 000 000
80%的数据中, 1 <= ci <= 50
100%的数据中,1 <= ci <= 1 000 000 000

Solution

60%

对于60%的数据,我们很容易想到设二维的dp,即设f[i][j]表示序列A从后往前做到了第i个位置,最后一次直接花费的位置在j的最小费用.

要更新f[i][i]显然需要再枚举f[i+1][j]|(j>i)然后加上ai
要更新f[i][j]|(j>i)其实就是f[i+1][j]+(ji)

90%

研究60%的方法,我们发现可以滚动.

然后发现对于枚举的一个i,其实当有j>i时,f[i,j]=f[i+1,j]+(ji),所以这里可以间接求出来,于是一开始想到了用线段树去维护,然后找min,后来发现因为加的值不一样,所以不能用线段树区间修改;

首先我们把二维变成一维后,显然我们需要先更新f[i]的值,用f[i+1n]去更新,然后再把f[i+1n]的值加上(ji),然后发现这个很麻烦,每次都加,我们可以直接不加,在算第j个点对于i的贡献,其实就是(ji)(ji1)/2.

于是我们可以加一个小小的决策优化,即如果上一次是由j更新i,那么j以后的值都没有可能继续更新,直接break就好了.

100%

正确的做法有两种:

①斜率优化

考虑一个决策点j比决策点k优,当且仅当

f(j)+(ji)(ji1)/2<f(k)+(ki)(ki1)/2
时,化简可得
f(j)f(k)+j2k2j+k2jk<i

决策点j比决策点k要优,设上述式子的值为g(j,k),那么我们可以用一个队列来单调的维护g(j,k).

保证对于

i>g(d1,d2)>g(d2,d3)>>g(dlen1,dlen)

那么每次的d1就可以更新对应的fi值了.

注意还要判断fi是否可以更新队尾,保证值的递减.

由于第一次打斜率优化,贴一下代码吧:

var        a,d,f:array[0..1000000] of int64; //注意不要用qword,不资兹负数        n,j:int64;        i:longint;function min(x,y:int64):int64; begin if x<y then exit(x) ; exit(y); end;function g(x,y:int64):real; begin exit(((f[x]-f[y]+(sqr(x)-sqr(y)-x+y)/2))/(x-y)) end;begin        readln(n);        for i:=1 to n do                read(a[i]);        d[0]:=1; d[1]:=n;        j:=1; f[n]:=a[n];        for i:=n-1 downto 0 do        begin                while (j<d[0]) and (g(d[j],d[j+1])>=i) do inc(j);  //更新队首                f[i]:=f[d[j]]+a[i]+(d[j]-i)*(d[j]-i-1) div 2;                while (j<d[0]) and (g(d[d[0]-1],d[d[0]])<=g(d[d[0]],i)) do dec(d[0]); //更新队尾                inc(d[0]);                d[d[0]]:=i;  //加入队列里        end;        write(f[0]);end.

②优先队列

——膜拜膜拜詹总的做法;

原创粉丝点击