SZOJ 150 生命衆籌

題目描述
有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優
View Code

而後利用隊列優化

注意在每次計算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;
}
View Code
相關文章
相關標籤/搜索