Loj3460【USACO 2021.1 P】T2. Minimum Cost Paths
阿新 • • 發佈:2021-08-19
【USACO 2021.1 P】T2. Minimum Cost Paths
題目大意
一個 \(N \times M\) 的矩陣( \(2 \le N \le 10^9\) , \(2 \le M \le 2 \times 10^5\) ),走第 \(x\) 行距離為 \(x^2\) , 走第 \(y\) 列距離為 \(c_y\) , 問從 \(\left( 1,1 \right)\) 到 \(x , y\) 的最短距離,多組詢問。
解法
一眼當然是 DP,
容易發現對於每一列,總有一個分界點,稱其為拐點,使得 在分界點前,從前一列轉移來;在分界點後,從前一行轉移來。
考慮找出這個分界點,即可快速確定 DP 值。
由於拐點可能從若干列前轉移過來,所以動態維護拐點的圖形,離線詢問,推推式子即可。
#include<bits/stdc++.h> #define fo(i,a,b) for(int i=a;i<=b;++i) #define fd(i,a,b) for(int i=a;i>=b;--i) #define ll long long using namespace std; const int N=2e5+10; int n,m,T,cnt; ll f[N],c[N],ans[N]; struct node{int x,y;ll v;}a[N]; struct que{int x,y,id;}q[N]; ll sqr(ll x){return x*x;} bool cmp(que a,que b){return a.y<b.y;} int main(){ freopen("data.in","r",stdin); freopen("data.out","w",stdout); scanf("%d%d",&n,&m); fo(i,1,m)scanf("%lld",&c[i]); a[cnt=1]=(node){1,1,0}; f[1]=1; scanf("%d",&T); fo(i,1,T){ scanf("%d%d",&q[i].x,&q[i].y); q[i].id=i; } sort(q+1,q+T+1,cmp); int it=1; for(;it<=T && q[it].y==1;++it){ ans[q[it].id]=c[1] * (q[it].x - 1); } a[cnt=1]=(node){1,1,0}; fo(i,2,m){ while(c[i] - c[a[cnt].y] < 2ll * a[cnt].x * (i - a[cnt].y) && cnt>1)--cnt; int x=a[cnt].x,y=a[cnt].y; if(c[i] - c[y] <= 2ll * x * (i - y))a[cnt].v+=sqr(x) * (i-y),a[cnt].y=i; else{ int t=((c[i] - c[y] ) / (i - y) + 1) /2 -x; ll v=a[cnt].v + t * c[y] + sqr(x+t) * (i - y); a[++cnt]=(node){x + t ,i ,v}; f[i]=cnt; } for(;it<=T && q[it].y==i;++it){ x=q[it].x; if(x>=a[cnt].x){ ans[q[it].id]=a[cnt].v + c[i] * (x - a[cnt].x); continue; } int l=1,r=cnt,mid; while(l<r-1){ mid=l+r>>1; if(a[mid].x<=x)l=mid; else r=mid; } ans[q[it].id]=a[l].v + c[a[l].y] * (x - a[l].x) + sqr(x) * (i - a[l].y); } } fo(i,1,T)printf("%lld\n",ans[i]); return 0; }