USACO21JAN Minimum Cost Paths P

洛谷P7294 [USACO21JAN] Minimum Cost Paths P

题目大意

有一个 n × m n\times m n×m的方阵,记帝 x x x行第 y y y列的格子为 ( x , y ) (x,y) (x,y),对于每一列有一个代价 c y c_y cy。

棋子从 ( 1 , 1 ) (1,1) (1,1)出发,当处于 ( x , y ) (x,y) (x,y)的时候,有以下两种选择:

  • 如果 y < m y<m y<m,即棋子不在最后一列,则棋子可以以 x 2 x^2 x2的代价移动到下一列
  • 如果 x < n x<n x<n,即棋子不在最后一行,则棋子可以以 c y c_y cy的代价移动到下一行

有 q q q次询问,每次给定目标位置 ( x i , y i ) (x_i,y_i) (xi,yi),求棋子从 ( 1 , 1 ) (1,1) (1,1)移动到 ( x i , y i ) (x_i,y_i) (xi,yi)的最小代价。

对于 25 % 25\% 25%的数据,满足 c 2 > c 3 > ⋯ > c m c_2>c_3>\cdots >c_m c2>c3>⋯>cm

2 ≤ n ≤ 1 0 9 , 2 ≤ m ≤ 2 × 1 0 5 , 1 ≤ q ≤ 2 × 1 0 5 , 1 ≤ c y ≤ 1 0 9 2\leq n\leq 10^9,2\leq m\leq 2\times 10^5,1\leq q\leq 2\times 10^5,1\leq c_y\leq 10^9 2≤n≤109,2≤m≤2×105,1≤q≤2×105,1≤cy≤109

题解

有一个部分分是 c 2 > c 3 > ⋯ > c m c_2>c_3>\cdots >c_m c2>c3>⋯>cm,那么最优路径一定是先从 ( 1 , 1 ) (1,1) (1,1)向下走到 ( t , 1 ) (t,1) (t,1),然后向右走到 ( t , y ) (t,y) (t,y),最后向下走到 ( x , y ) (x,y) (x,y)。为什么呢?因为在这种情况下,最优路径中只能是在第一列或第 y y y列向下走,否则将向下走的部分平移到第 y y y行一定更优。

我们来计算一下这条路径的代价。这条路径是 ( 1 , 1 ) → ( t , 1 ) → ( t , y ) → ( x , y ) (1,1)\to (t,1)\to (t,y)\to (x,y) (1,1)→(t,1)→(t,y)→(x,y),那么代价为 ( t − 1 ) × c 1 + ( y − 1 ) × t 2 + ( x − t ) × c y (t-1)\times c_1+(y-1)\times t^2+(x-t)\times c_y (t−1)×c1+(y−1)×t2+(x−t)×cy。

我们发现,这是一个关于 t t t的二次函数 f ( t ) = ( y − 1 ) × t 2 + ( c 1 − c y ) × t + x × c y − c 1 f(t)=(y-1)\times t^2+(c_1-c_y)\times t+x\times c_y-c_1 f(t)=(y−1)×t2+(c1−cy)×t+x×cy−c1,当 t t t取 c y − c 1 2 ( y − 1 ) \dfrac{c_y-c_1}{2(y-1)} 2(y−1)cy−c1时 f ( t ) f(t) f(t)取得最小值。而因为要求的 t t t是整数,所以当代价最小时 t t t的值为与 c y − c 1 2 ( y − 1 ) \dfrac{c_y-c_1}{2(y-1)} 2(y−1)cy−c1相差最小的整数的值,也就是 c y − c 1 2 ( y − 1 ) \dfrac{c_y-c_1}{2(y-1)} 2(y−1)cy−c1四舍五入后的值。

进一步地,我们发现,对于任意两列 y 1 , y 2 y_1,y_2 y1,y2,设 x 0 x_0 x0为 c y 2 − c y 1 2 ( y 2 − y 1 ) \dfrac{c_{y_2}-c_{y_1}}{2(y_2-y_1)} 2(y2−y1)cy2−cy1四舍五入后的值,我们在第 x 0 x_0 x0行转移是最优的,记 P ( y 1 , y 2 ) = x 0 P(y_1,y_2)=x_0 P(y1,y2)=x0。

我们先忽略 x x x的限制,对于每一组询问中,从 1 1 1到 y y y的最小代价的路径一定经过若干个 y 1 , y 2 , ... , y k y_1,y_2,\dots,y_k y1,y2,...,yk,其中从 y i → y i + 1 y_i\to y_{i+1} yi→yi+1都选择 P ( y i , y i + 1 ) P(y_i,y_{i+1}) P(yi,yi+1)对应的路径。

那么,我们可以对 y y y离线,用单调栈维护当前转移路径上所有的 y i y_i yi。因为 c y 2 − c y 1 2 ( y 2 − y 1 ) \dfrac{c_{y_2}-c_{y_1}}{2(y_2-y_1)} 2(y2−y1)cy2−cy1可以看做斜率 × 1 2 \times \dfrac 12 ×21,而随着 y y y增加,转折的部分 P ( y i , y i + 1 ) P(y_i,y_{i+1}) P(yi,yi+1)也是递增的,所以单调栈中需要满足 y i y_i yi斜率递增。可以用调整法来证明这一贪心策略的正确性。

注意因为 x 0 x_0 x0在 [ 1 , n ] [1,n] [1,n]的范围内,所以要将 P ( y i , t i + 1 ) P(y_i,t_{i+1}) P(yi,ti+1)调整到 [ 1 , n ] [1,n] [1,n]上,对 1 1 1取 max ⁡ \max max再对 n n n取 max ⁡ \max max即可。

再来考虑 x x x的限制。对于每次询问中 x x x的限制,我们在单调栈中二分找到最后一段 斜率 × 1 2 ≤ x \times \dfrac 12\leq x ×21≤x 的部分,那么 ( x c , y c ) (x_c,y_c) (xc,yc)到 ( x , y ) (x,y) (x,y)也是先向下再向右最后向下,设末尾为 ( x c , y c ) (x_c,y_c) (xc,yc),则再按上面的方式用二次函数求 ( x c , y c ) (x_c,y_c) (xc,yc)到 ( x , y ) (x,y) (x,y)的最小代价即可。

时间复杂度为 O ( q log ⁡ m ) O(q\log m) O(qlogm)。

code

cpp 复制代码
#include<bits/stdc++.h>
#define ll __int128
using namespace std;
const int N=200000;
int n,m,Q,tp=1,s[N+5];
long long c[N+5],sum[N+5],ans[N+5];
struct node{
	long long x,y;
	int id;
};
vector<node>q[N+5];
long long slope(int x,int y){
	int re=round((c[y]-c[x])*1.0/(2*(y-x)));
	re=max(re,1);re=min(re,n);
	return re;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		scanf("%lld",&c[i]);
	}
	scanf("%d",&Q);
	for(int i=1,x,y;i<=Q;i++){
		scanf("%d%d",&x,&y);
		q[y].push_back((node){x,y,i});
	}
	for(node v:q[1]){
		ans[v.id]=c[1]*(v.x-1);
	}
	s[tp]=1;sum[tp]=0;
	for(int i=2;i<=m;i++){
		while(tp>1&&slope(s[tp-1],s[tp])>=slope(s[tp],i)) --tp;
		long long now=slope(s[tp],i),lst;
		if(tp==1) lst=1;
		else lst=slope(s[tp-1],s[tp]);
		sum[tp+1]=sum[tp]+(now-lst)*c[s[tp]]+(i-s[tp])*now*now;
		s[++tp]=i;
		for(node v:q[i]){
			int l=2,r=tp,mid,re;
			while(l<=r){
				mid=l+r>>1;
				if(slope(s[mid-1],s[mid])<=v.x) l=mid+1;
				else r=mid-1;
			}
			re=l-1;
			long long tmp;
			if(re==1) tmp=1;
			else tmp=slope(s[re-1],s[re]);
			ans[v.id]=sum[re]+(v.x-tmp)*c[s[re]]+(i-s[re])*v.x*v.x;
		}
	}
	for(int i=1;i<=Q;i++){
		printf("%lld\n",ans[i]);
	}
	return 0;
}
相关推荐
无 证明37 分钟前
new 分配空间;引用
数据结构·c++
别NULL5 小时前
机试题——疯长的草
数据结构·c++·算法
CYBEREXP20086 小时前
MacOS M3源代码编译Qt6.8.1
c++·qt·macos
yuanbenshidiaos6 小时前
c++------------------函数
开发语言·c++
yuanbenshidiaos6 小时前
C++----------函数的调用机制
java·c++·算法
tianmu_sama7 小时前
[Effective C++]条款38-39 复合和private继承
开发语言·c++
羚羊角uou7 小时前
【C++】优先级队列以及仿函数
开发语言·c++
姚先生977 小时前
LeetCode 54. 螺旋矩阵 (C++实现)
c++·leetcode·矩阵
FeboReigns7 小时前
C++简明教程(文章要求学过一点C语言)(1)
c语言·开发语言·c++
FeboReigns7 小时前
C++简明教程(文章要求学过一点C语言)(2)
c语言·开发语言·c++