题目描述
有m个法师,n座山,法师在山上膜蛤,给他续命ios
第i座山到前一座的距离是di, 第i个法师在第hi座山上,他续命须要的时间是ti (也就是ti时间以后这个生命就能够收集了,若是没有及时收集,就会在原地等待收集)ide
他有p个手下从第一座山出来收集法师们续出来的生命,一路走过去,收走准备好的生命之力,若是这个生命之力没收集好,他就直接过去函数
你要合理手下们出发的时间优化
由于生命之力很是珍贵,因此一旦产生就要尽快收集,如今他想知道,续好的生命之力等待收集的时间总和最少是多长spa
移动的时间就是移动的距离code
输入格式
第一行输入三个整数 n, m, p (2≤ n ≤ 10^5, 1 ≤ m ≤ 10^5, 1 ≤ p ≤ 100 ).blog
第二行输入n−1个数d2, d3, …, dn(1 ≤ di ≤ 10^4)排序
接下来m行每行两个整数hi,ti(1 ≤ hi ≤ n, 0 ≤ ti≤ 10^9)队列
输出格式
输出一个整数,表示生命之力等待收集的时间之和it
输入样例
4 6 2
1 3 5
1 0
2 1
4 9
1 10
2 10
3 12
输出样例
3
数据规模
对于40%的数据,n,m≤300
对于100%的数据 2≤ n ≤ 10^5, 1 ≤ m ≤ 10^5, 1 ≤ p ≤ 100,1 ≤ di ≤ 10^4,1 ≤ hi ≤ n, 0 ≤ ti≤ 10^9
先将每一个法师产生生命之力的时间减去他到起点的距离,ai=ti-si,这样就至关于在等待ai秒后出发刚好能收集到ti,这样的好处是,咱们只需对ai排序后处理,由于对于ai若能被收集到,则aj必定能被收集到(j<i),只是时间上不恰好
w(i,j)=(j-i)*a[j]-(sum[j]-sum[i])
dp[i][j]=min{dp[i-1][k]+w(k,j)}
80分写出dp式子便可
100分须要斜率优化
斜率优化就是要保证一个图像,其中相邻的段数自小到大保证上升斜率
一下是证实,和计算斜率函数f(u,v)的求解
//由于原本在txt里打的因此复制上来粘成一坨。。。因此放在代码里好了
dp[i][j]=min(dp[i-1][k]+w(k,j)) dp[i][j]=min(dp[i-1][k]+(j-k)*a[j]-(sum[j]-sum[k])) 令 决策u,v u<v dp[i][u]==dp[i-1][u]+(j-u)*a[j]-(sum[j]-sum[u]).....1 dp[i][v]==dp[i-1][v]+(j-v)*a[j]-(sum[j]-sum[v]).....2 1-2-->dp[i][u]-dp[i][v]=dp[i-1][u]+(j-u)*a[j]-(sum[j]-sum[u])-(dp[i-1][v]+(j-v)*a[j]-(sum[j]-sum[v])) -->dp[i][u]-dp[i][v]==dp[i-1][u]-dp[i-1][v]+(v-u)*a[j]+sum[u]-sum[v] -->dp[i][u]-dp[i][v]==dp[i-1][u]+sum[u]-(dp[i-1][v]+sum[v])+(v-u)*a[j] 若dp[i][u]-dp[i][v]<0,则 dp[i-1][u]+sum[u]-(dp[i-1][v]+sum[v])+(v-u)*a[j]<0 .....u比v优 dp[i-1][u]+sum[u]-(dp[i-1][v]+sum[v])+(v-u)*a[j]<0 u<v --> .....3 a[j]<{dp[i-1][u]+sum[u]-(dp[i-1][v]+sum[v])}/(u-v) a[j]>0,因此当u<v,决策u比v更优 f(u,v)={dp[i-1][u]+sum[u]-(dp[i-1][v]+sum[v])}/(u-v),若知足a[j]<f(u,v),u比v优,反之v比u优
而后利用队列优化
注意在每次计算dp的值前,先扫描一遍队列,若f(head,head+1)<a[j],则删去head(head+1比head更优,则更新时不可能再用到head)
再计算完dp值后,对于当前的j咱们要放入队尾,此时咱们面临着两种状况
f(tail,j)>f(tail-1,tail),则直接加入队尾,由于斜率上升符合条件
反之则删除tail,知道知足上一行为止
注意计算f(x)函数时要分红分母和分子,防止分母有0
代码:(贼短)
#include<iostream> #include<cstdlib> #include<cstdio> #include<algorithm> using namespace std; int n,m,p,d[100001],h[100001],t[100001],head,tail,que[100001]; long long ans=1000000,s[100001],a[100001],f[101][100001],sum[100001]; inline long long up(int i,int u,int v){ return f[i-1][u]+sum[u]-f[i-1][v]-sum[v]; } inline long long down(int x,int y){ return x-y; } inline void Jimmy(){ scanf("%d%d%d",&n,&m,&p); for(int i=2;i<=n;i++) scanf("%d",&d[i]),s[i]=s[i-1]+d[i]; for(int i=1;i<=m;i++) scanf("%d%d",&h[i],&t[i]),a[i]=t[i]-s[h[i]]; sort(a+1,a+1+m); for(int i=1;i<=m;i++) sum[i]=sum[i-1]+a[i]; for(int i=1;i<=m;i++) f[1][i]=i*a[i]-sum[i]; ans=f[1][m]; for(int i=2;i<=p;i++){ head=tail=1;que[1]=0; for(int j=1;j<=m;j++){ while(head<tail && up(i,que[head],que[head+1])>down(que[head],que[head+1])*a[j]) head++; f[i][j]=f[i-1][que[head]]+(j-que[head])*a[j]-(sum[j]-sum[que[head]]); while(head<tail && up(i,que[tail],j)*down(que[tail-1],que[tail])<up(i,que[tail-1],que[tail])*down(que[tail],j)) tail--; que[++tail]=j; } if(ans>f[i][m]) ans=f[i][m]; } printf("%lld\n",ans); } int main(){ Jimmy(); return 0; }