題目描述
有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; }