将小猫对应对应到起点就是很板的题目了额
比如miao是在h[i] ,h[i]这个点距离原点的距离是x,miao出现的时候为y,对应以后的距离就是y-x,说明从y-x时刻出发能达到该miao
样例对应以后排序就是 0 0 0 8 9 10 记为d数组
那你选择从0 和 10 出发,花费就是 0+0+0+2+1+0 =3
- 很容易得出dp转移方程了额 : DP[K][I] = MIN{ DP[K-1][J] + COST(J,I) } (j < i ; cost(j,i) 是指 在i处出发的人要取得从 j+1 到i 的miao的花费)
- 定义 sumd, sumd[i] = segma( d[k]) ( 1<=k <=i) ,所以 cost(j,i) 就等于 d[i]*(i-j) - (sumd[i]-sumd[j])
- 再展开dp转移方程: dp[k][i] = min( dp[k-1][j] + d[i] * i - d[i]*j - sumd[i] + sumd[j])
- 化简之后就是: dp[k-1][j] + sumd[j] = d[i] * j + dp[k][i] -d[i]*i + sumd[i]
- 令 y = dp[j-1][j] + sumd[j] , x = j, G = dp[k][i] - d[i] * i + sumd[i] ; 那么原式变为y = d[i] * x + G
其中G就是我们间接要求的,剩下就是用栈维护一个凸包,将复杂度降为 n^2
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#define fr(i,s,n) for(int i = s;i<n;++i)
typedef long long ll;
using namespace std;
ll dp[110][101000];
ll d[100010];
ll sumd[100010];
int n,m,p;
int dis[100010];
int tot = 1;
struct STA{
struct Sta{
ll x,y;
Sta(ll a,ll b):x(a),y(b){}
Sta(){}
}s[100010];
int top,now;
void init(){
top = -1;
now = 0;
}
void P(ll a,ll b){
if( top < 1) s[++top] = Sta(a,b);
else{
while(1){
if( top == 0){
s[++top] = Sta(a,b);
break;
}
ll y1 = b-s[top].y;
ll x1 = a-s[top].x;
ll y2 = s[top].y - s[top-1].y;
ll x2 = s[top].x - s[top-1].x;
if( y1 * x2 > y2 * x1){
s[++top] = Sta(a,b);
break;
}
else{
top --;
}
}
}
}
}S;
double cal(int x){
return double (S.s[x+1].y - S.s[x].y)/ double(S.s[x+1].x - S.s[x].x );
}
int main(){
scanf("%d%d%d",&n,&m,&p);
fr(i , 2, n+1){
scanf("%d",&dis[i]);
dis[i] = dis[i-1]+dis[i];
}
int h,t;
fr(i , 0 ,m){
scanf("%d%d",&h,&t);
d[tot++] = t-dis[h];
}
sort(d+1,d+tot);
S.init();
fr(i , 1 ,tot) sumd[i] = sumd[i-1] + d[i];
fr(i , 1 ,tot) dp[1][i] = d[i] * i - sumd[i];
fr( k ,2, p+1){
fr(i ,k,tot){
S.P( i-1 , dp[k-1][i-1] + sumd[i-1] );
while( S.now < S.top && cal(S.now) < double(d[i]) ){
S.now++;
}
dp[k][i] = S.s[S.now].y - S.s[S.now].x * d[i] + i*d[i] - sumd[i];
}
S.init();
}
if( p >=tot){
puts("0");
return 0;
}
printf("%lld\n",dp[p][tot-1]);
return 0;
}