1. 程式人生 > 其它 >Loj3460【USACO 2021.1 P】T2. Minimum Cost Paths

Loj3460【USACO 2021.1 P】T2. Minimum Cost Paths

【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;
}