【USACO 2021JAN P】Minimum Cost Paths

【USACO 2021JAN P】Minimum Cost Paths

by AmanoKumiko

Description

平面直角坐标系中(其实是给出\(N*M\)的方阵)

\((x,y)\)可以用\(x^2\)的代价使\(y+1\),可以用\(c_y\)的代价使\(x+1\)

给出\(Q\)组询问

每次求出\((1,1)\)\((xi,yi)\)所需的最小代价

Input

第一行\(N\)\(M\)

第二行\(M\)个数\(c_1,c_2...c_m\)

第三行\(Q\)

最后\(Q\)行为询问

Output

\(Q\)行,表示答案

Sample Input

5 4
1 100 100 20
20
1 1
2 1
3 1
4 1
5 1
1 2
2 2
3 2
4 2
5 2
1 3
2 3
3 3
4 3
5 3
1 4
2 4
3 4
4 4
5 4

Sample Output

0
1
2
3
4
1
5
11
19
29
2
9
20
35
54
3
13
29
49
69

Data Constraint

\(2 \le N \le 10^9\)\(2 \le M,Q \le 2·10^5\)

Solution

对于每个询问,可以发现答案的路径是一条折线,考虑维护

离线,按\(y\)排序

我们记录折线上的所有拐点(即图中的红点)

对于新加入的\(c_y\)

设当前拐点为\((u,v)\)

\(c_y\)优于\(c_v\)

考虑从\((u,v)\)往上走

则有\((y-v)·u^2+c_y·t<(y-v)(u+t)^2+c_v·t\)

化简得\(\frac{c_y-c_v}{y-v}<2u+t\)

那么当\(\frac{c_y-c_v}{y-v}<2u\)时,\(c_y优于c_v\),删除\(c_v\)

否则,考虑找出新的拐点

若从紫色路径往上劣于走它左边的黑色路径

\(c_v·t+(u+t)^2(y-v)+c_y<c_v·(t+1)+(u+t+1)^2(y-v)\)

化简得\(\frac{c_y-c_v}{y-v}<2(u+t)+1\)

移项算出最小的\(t\),于是得到新拐点\((u+t,y)\)

维护拐点的同时,算出到每个拐点的答案(前缀和)

对于询问,二分找到最近的拐点即可

Code

#include<bits/stdc++.h>
using namespace std;
#define F(i,a,b) for(int i=a;i<=b;i++)
#define Fd(i,a,b) for(int i=a;i>=b;i--)
#define Ld long double
#define LL long long
#define N 200010

LL c[N],ans[N],sum[N];
struct node{LL x,y;int pos;}ask[N];
struct point{LL x,y;}tr[N];
int n,m,q,h,l,r,mid,top;

Ld Slope(LL x,LL y){return 1.0*(c[x]-c[y])/(x-y);}

bool cmp(node a,node b){return a.y<b.y||a.y==b.y&&a.x<b.x;}

LL cost(point a,point b){return (b.x-a.x)*c[a.y]+(b.y-a.y)*b.x*b.x;}

void insert(LL p){
	while(Slope(p,tr[top].y)<2.0*tr[top].x)top--;
	LL w=ceil((Slope(p,tr[top].y)-1.0)/2.0+0.000001);
	top++;
	tr[top]=(point){top==1?1:w,p};
	sum[top]=sum[top-1]+cost(tr[top-1],tr[top])-(top==1);
}

int main(){
	scanf("%d%d",&n,&m);
	F(i,1,m)scanf("%lld",&c[i]);
	scanf("%d",&q);
	F(i,1,q)scanf("%lld%lld",&ask[i].x,&ask[i].y),ask[i].pos=i;
	sort(ask+1,ask+q+1,cmp);
	tr[top=h=1]=(point){1,1};
	F(i,1,q){
		while(h+1<=ask[i].y)insert(++h);
		l=0;r=top+1;
		while(l<r-1)mid=l+r>>1,tr[mid].x>ask[i].x?r=mid:l=mid;
		ans[ask[i].pos]=sum[r-1]+cost(tr[r-1],(point){ask[i].x,ask[i].y});
	}
	F(i,1,q)printf("%lld\n",ans[i]);
	return 0;
}
posted @ 2021-08-19 20:19  冰雾  阅读(105)  评论(0编辑  收藏  举报