HZOJ Function

比較神仙的一道dp,考試的時候還覺得是打表找規律啥的。ios

咱們從新描述一下這道題:一個10 9 × n的網格,每一個格子有一個權值,每一列格子的權值都是相同的。從一個起點開始,每次能夠向上走一格或者向左上角走一格,直到走到最上面一行爲止,你須要最小化通過的格子的總權值。ide

然而我並無看出來。函數

首先咱們能夠發現一些顯然的性質,最優的路徑之一必定形如:先往左上走若干步(可能不走),到達權值較小的一列後,一直往上走到頂。對於每一個詢問,枚舉從起點出發最終會到達哪一列,就能夠獲得一個O(nq)的作法。spa

然而我也沒有想到……code

算了直接把做者的題解全放出來吧:blog

對於任意1 ≤ i ≤ j ≤ n, 從(x, j)出發最終到達第i列而後走到頂的代價,能夠表示爲一個關於x的一次函數,咱們只關心這些一次函數的最小值,也就是這些直線造成的下凸殼。咱們獲得一個思路:將詢問離線,按y從小到大排序,從最左邊開始每次加入一條直線,維護下凸殼,而後在凸殼上二分便可獲得答案。怎麼維護下凸殼呢?對於一個點(x, y),它要麼繼承上一列x − 1的決策,要麼就直接往上走到頂。而且咱們發現,第二種狀況只會出如今從頂端開始連續的一段中。因而咱們只須要用棧維護凸殼便可。O((n + q) log n).排序

剛開始沒怎麼看懂,好像個人作法和題解也不是很同樣,其實如今還有一些細節沒有搞明白……繼承

首先看暴力的式子:$ans=min(ans,sum[y(i)]-sum[j]+(x(i)-y(i)+j)*A[j])$get

在y固定時,他是一個關於x的一次函數,即$y=kx+b$的形式,設走到j時中止而後向上走,那麼$k=A[j],b=sum[y]-sum[j]+(j-y)*A[j]$string

對於每個j都是一條直線,那麼這些直線構成了一個上凸殼。

咱們能夠用On的複雜度枚舉y,用棧維護凸殼(添加直線是加在了座標系的最左邊),考慮y增長會給直線形成什麼影響,只會使直線的截距發生改變而斜率不變,因此原來的凸殼仍然是對的。

那麼考慮如何吧j=y的這條之間加入凸殼,首先將斜率大於這條直線的棧頂直線彈掉,而後交點也得是單調的,繼續彈掉不合法的,(本身yy一下座標系,橫軸是詢問的x,縱軸爲最優解),

而後處理當前y的詢問,直接二分棧找到當前x在座標系中對應的直線就能夠了(必定注意棧頂實際上是座標軸最左邊的直線)。

放下代碼(稍噁心):

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #define st sta[top]
 6 #define sm sta[mid]
 7 #define sm1 sta[mid+1]
 8 #define st1 sta[top-1]
 9 #define int LL
10 #define LL long long
11 using namespace std;
12 struct ques
13 {
14     int x,y,id;
15     #define x(i) que[i].x
16     #define y(i) que[i].y
17     #define id(i) que[i].id
18     friend bool operator < (ques a,ques b)
19     {return a.y<b.y;}
20 }que[500010];
21 int n,A[500010],q,maxx;
22 LL sum[500010],al[500010];
23 LL sta[500010],top;
24 double getx(int k1,int k2,int j1,int j2){return (double)(j2-j1)/(double)(k1-k2);}
25 inline int read();
26 signed main()
27 {    
28 //    freopen("function2.in","r",stdin);
29 //    freopen("out.out","w",stdout);
30     
31     n=read();
32     for(int i=1;i<=n;i++)A[i]=read(),sum[i]=sum[i-1]+A[i];
33     q=read();
34     for(int i=1;i<=q;i++)x(i)=read(),y(i)=read(),id(i)=i;
35     sort(que+1,que+q+1);
36     
37     int now=1;
38     for(int y=1;y<=n;y++)
39     {    
40         while(top&&A[sta[top]]>=A[y])top--;
41         while(top>1&&
42                 getx(A[y],A[st],0,sum[y]-sum[st]+A[st]*(st-y))
43               >=getx(A[st1],A[st],sum[y]-sum[st1]+A[st1]*(st1-y),sum[y]-sum[st]+A[st]*(st-y))
44                 )top--;
45         sta[++top]=y;
46         for(;y(now)==y&&now<=q;now++)
47         {
48             int l=1,r=top,mid;
49             while(l<r)
50             {
51                 mid=(l+r)>>1;
52                 double tx=getx(A[sm],A[sm1],sum[y]-sum[sm]+A[sm]*(sm-y),sum[y]-sum[sm1]+A[sm1]*(sm1-y));
53                 if(x(now)<=tx)l=mid+1;
54                 else r=mid;
55             }
56             mid=l;
57             al[id(now)]=sum[y]-sum[sm]+A[sm]*(x(now)-y+sm);
58         }
59         
60     }
61     for(int i=1;i<=q;i++)printf("%lld\n",al[i]);
62 }
63 inline int read()
64 {
65     int s=0,f=1;char a=getchar();
66     while(a<'0'||a>'9'){if(a=='-')f=-1;a=getchar();}
67     while(a>='0'&&a<='9'){s=s*10+a-'0';a=getchar();}
68     return s*f;
69 }
View Code
相關文章
相關標籤/搜索