[AGC011F] Train Service Planning [線段樹優化dp+思惟]

思路

模意義

這題真tm有意思
我上下樓梯了半天作出來的qwqios

首先,考慮到每K分鐘有一輛車,那麼能夠把全部的操做都放到模$K$意義下進行數組

這時,咱們只須要考慮兩邊的兩輛車就行了。函數

定義一些稱呼:

上行:從$0$~$n$的車優化

下行:從$n$~$0$的車spa

定義數組:

$p[i](i=0...n)$表示上行車在站臺$i$的停車時長code

$q[i](i=0...n)$表示下行車在站臺$i$的停車時長get

$a[i](i=0...n-1)$表示第$i$的站臺與第$i+1$個站臺中間的行駛時間(也就是題目給定的數組)string

咱們再定義$S(a,i)$表示數組$a$從零開始到$i$的前綴和函數,即:it

$S(a,i)=\sum_{j=0}^i a[j]$io

區間不交

考慮兩輛車合法的狀況。顯然,他們倆在每一段鐵路上行駛的時間是一個區間。雙向鐵路不用管,必定合法,咱們發現若是單向鐵路上合法,那麼上下行的車在某一段單向鐵路上行駛的區間確定不相交

咱們把上下行車輛在鐵路$k$上的行駛時間區間寫出來:

上行:$(S(a,i-1)+S(p,i),S(a,i)+S(p,i))$

下行:$(-S(a,i-1)-S(q,i),-S(a,i)-S(q,i))$

注意這裏的下行部分是一個很巧妙的轉化:由於咱們在模意義下運算,因此下行部分原本應該是用兩個後綴和加在一塊兒的,可是這樣上下行式子不統一

因此,咱們把後綴和看作總和減掉前綴和,那麼總和能夠調整爲$K$的倍數,就沒掉了,因此就是負的前綴和

這兩段區間若是不交,那麼顯然任意一個端點不在另一個區間裏面

這樣咱們能夠獲得一批不等式,大概相似於這個形式:

$-2S(a,i-1)>S(p,i)+S(q,i)>-S(a,i-1)-S(a,i)$

最終化一下,能夠獲得這個結論:

$S(p,i)+S(q,i) \notin (-2\ast S(a,i),-2\ast S(a,i-1))$

能夠看到對於每一個$i$,不可選的區間是固定的

那麼咱們考慮在模$K$的意義下,右邊這個區間的補集,咱們設它爲$[L[i],R[i]]$

$S(p,i)+S(q,i) \in [L[i],R[i]]$

問題變化

咱們發現,本題的答案,實際上就是最小化的$S(p,n)+S(q,n)+2\ast S(a,n)$,等價於最小化$S(p,n)+S(q,n)$

這樣,咱們能夠把問題轉化爲:

給定$n$個區間,任選起點,走$n$步,第$i$步須要落在區間$i$中,求最小總路徑長度

注意這裏的「走」實際上,從一個大的走向小的,是要走到$K$,而後從$0$再出來(由於咱們是模$K$意義下的)

這樣就能夠變成一個$DP$問題了

線段樹優化$DP$

咱們如今考慮新的這個問題,顯然可使用$dp$來作

首先有一個貪心結論:若是起點已經肯定了,那麼每次須要走的時候,走到下一個區間的左端點確定最優,證實顯然

那麼咱們能夠先預處理出來在每一個起點出發後,一直走左端點直到走完,我須要的最小距離

預處理

定義$dp[i]$表示對於區間$[L[i],R[i]]$的左端點$L[i]$而言,一直走左端點到$n$的最短路徑

那麼咱們倒着從$n$到$1$推這個$dp$

每推完一個$dp[i]$,咱們就把區間$[L[i],R[i]]$的補集在線段樹上的值,覆蓋爲$i$

而後推$dp[i]$時,每次查詢$L[i]$位置的線段樹上的值,設這個值是$j$

那麼顯然,編號在區間$[i,j-1]$中的全部區間都覆蓋了$L[i]$這個點

因此直接在這裏不動,就能夠走完這些區間了

那麼咱們用$dp[j]+dis(L[i],L[j])$來更新$dp[i]$

而後再覆蓋區間,就完成了$dp$預處理過程

統計答案!

最後一步,咱們枚舉全部出現的$L[i]$和$R[i]$,並把它們做爲起點,求出答案。

顯然若是不選這些端點的話,答案只會更劣

統計出的答案,就是原來問題中的$S(q,n)+$S(p,n)$,也就是咱們要最小化的東西

加上$2S(a,n)$輸出便可

作完啦~

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cassert>
#define ll long long
using namespace std;
inline ll read(){
    ll re=0,flag=1;char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') flag=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
    return re*flag;
}
ll n,K,cnt,a[200010],b[200010],tmp[500010],seg[500010],L[200010],R[200010],dp[200010],sum[200010];
void push(ll num){
    if(!seg[num]) return;
    seg[num<<1]=seg[num<<1|1]=seg[num];
    seg[num]=0;
}
void change(ll l,ll r,ll ql,ll qr,ll num,ll val){
    if(ql>qr) return;
    if(l>=ql&&r<=qr){seg[num]=val;return;}
    push(num);
    ll mid=(l+r)>>1;
    if(mid>=ql) change(l,mid,ql,qr,num<<1,val);
    if(mid<qr) change(mid+1,r,ql,qr,num<<1|1,val);
}
ll query(ll l,ll r,ll num,ll pos){
    if(l==r) return seg[num];
    push(num);
    ll mid=(l+r)>>1;
    if(mid>=pos) return query(l,mid,num<<1,pos);
    else return query(mid+1,r,num<<1|1,pos);
}
ll ask(ll pos){
    ll choose=query(1,cnt,1,pos);
    if(!choose) return 0;
    return (dp[choose]+(tmp[L[choose]]-tmp[pos]+K)%K);
}
ll erfen(ll val){
    ll l=1,r=cnt,mid;
    while(l<r){
        mid=(l+r)>>1;
        if(tmp[mid]>=val) r=mid;
        else l=mid+1;
    }
    assert(val==tmp[l]);
    return l;
}
int main(){
    n=read();K=read();ll i;
    for(i=1;i<=n;i++) {
        a[i]=read();b[i]=read();
        sum[i]=(sum[i-1]+a[i]);
        if(b[i]==2) continue;
        if(2*a[i]>K){
            puts("-1");return 0;
        }
    }
    for(i=n;i>=1;i--){
        if(b[i]==1){
            L[i]=(-2ll*sum[i-1]%K+K)%K;
            R[i]=(-2ll*sum[i]%K+K)%K;
        }
        else L[i]=0,R[i]=K-1;
        tmp[++cnt]=L[i],tmp[++cnt]=R[i];
    }
    sort(tmp+1,tmp+cnt+1);
    cnt=unique(tmp+1,tmp+cnt+1)-tmp-1;
    for(i=n;i>=1;i--){
        L[i]=erfen(L[i]);
        R[i]=erfen(R[i]);
        dp[i]=ask(L[i]);
        if(L[i]>R[i]) change(1,cnt,R[i]+1,L[i]-1,1,i);
        else change(1,cnt,1,L[i]-1,1,i),change(1,cnt,R[i]+1,cnt,1,i);
    }
    ll ans=dp[1];
    for(i=cnt;i>=1;i--) ans=min(ans,ask(i));
    printf("%lld\n",ans+sum[n]*2ll);
}
相關文章
相關標籤/搜索