比較神仙的一道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 }