这题真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[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)$输出便可
#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); }