有n+1个车站,n条轨道,第i条轨道联通i-1和i车站,通过它要花a[i]时间,这条轨道有b[i]=1或2条车道,也就是说,他是单向还是双向的。现在有两种火车,一种从0到n,一种从n到0,有无限辆,同种的发车间隔是K。
现在请你分别确定两种火车在每个站停留的时间,确保单车道不会有两辆车相向而行,且同一条轨道(不是车道),不能同时存在两部同向的火车。
要求最小化两种火车从起点到终点的时间和。
n<=1e5,K,a[]<=1e9
我们假设第一种每个站停留的时间(包括第0个,因为可以和另一种错开)为p[],第二种为q[]。发车间隔为K,说明我们可以在模K意义下进行。
双向轨道不用考虑,我们考虑方案对于单向轨道的合法性。(以下把双轨道忽略)
第一种火车在轨道m上开的时间为 (∑i=0..m−1a[i]+∑i=0..m−1p[i],∑i=0..ma[i]+∑i=0..m−1p[i]) ( ∑ i = 0.. m − 1 a [ i ] + ∑ i = 0.. m − 1 p [ i ] , ∑ i = 0.. m a [ i ] + ∑ i = 0.. m − 1 p [ i ] )
同样,第二种 (∑i=0..m−a[i]+∑i=0..m−1−p[i],∑i=0..m−1−a[i]+∑i=0..m−1−p[i]) ( ∑ i = 0.. m − a [ i ] + ∑ i = 0.. m − 1 − p [ i ] , ∑ i = 0.. m − 1 − a [ i ] + ∑ i = 0.. m − 1 − p [ i ] ) ,用负号定义的好处是等下化式子方便。
合法就要保证这些区间都不相交。区间不相交可以化为区间端点不在另一个区间内。
设a[i]的前缀和为s[i]。
化出来会是这样的东西:对于每个m, ∑i=0..m(p[i]+q[i])∉(−2∗s[m],−2∗(s[m]−a[m])) ∑ i = 0.. m ( p [ i ] + q [ i ] ) ∉ ( − 2 ∗ s [ m ] , − 2 ∗ ( s [ m ] − a [ m ] ) )
注意这里如果区间覆盖了整个区间(也就是0~k-1),那么无解。
设sum[i]表示p[i]+q[i]前缀和,在模K意义下补集转化就变成每个 m,sum[m]∈(L[m],R[m]) m , s u m [ m ] ∈ ( L [ m ] , R [ m ] ) (有可能是L>R的区间)。
我们现在要最小化sum[n]-sum[0]。ans=sum[n]-sum[0]+s[n]*2。
发现这个相当于,你在一个数轴上,有n个区间[L[i],R[i]],你可以自己确定初始位置,然后你要走n步,走第i步的时候要确保走到第i个区间内(模意义下的“之内”),然后最小化走的距离(这个不模)。
发现关键点只有区间的端点。就非常好做了。
先不管我们初始位置,先算一些东西。
倒过来枚举。设f[i]表示站在i区间左端点上,做完i~n这些区间走的最小距离。
转移很贪心:就是你找到第一个大于i且L[i]不在[L[j],R[j]]内的j,加上L[i]走到L[j]的代价,转移即可。
很显然走到R不优。
算完之后,我们枚举我们的起点在2*n个点中的哪一个,找到最大的j使得前j个区间都包含起点,用f[j]转移即可。
时间复杂度O(n log n)
#include
#include
#include
#include
#include
#include
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
#define cmin(a,b) (a=(a
typedef long long ll;
const int N=1e5+5,M=4e5+5,mo=1e9+7;
ll tr[M],a[N],b[N],s[N],n,K,i,j,k,L[N],R[N],v[M],m,f[N],ans;
void change(ll x,ll l,ll r,ll i,ll j,ll v)
{
if (i>j) return ;
if (l==i&&j==r)
{
tr[x]=v;
return ;
}
if (tr[x]) tr[x*2]=tr[x*2+1]=tr[x],tr[x]=0;
ll m=(l+r)/2;
if (m>=j) change(x*2,l,m,i,j,v);
else if (mx*2+1,m+1,r,i,j,v);
else change(x*2,l,m,i,m,v),change(x*2+1,m+1,r,m+1,j,v);
}
ll get(ll x,ll l,ll r,ll p)
{
if (l==r) return tr[x];
if (tr[x]) tr[x*2]=tr[x*2+1]=tr[x],tr[x]=0;
ll m=(l+r)/2;
if (m>=p) return get(x*2,l,m,p);else return get(x*2+1,m+1,r,p);
}
ll calc(ll x)
{
ll y=get(1,1,m,x);
if (!y) return 0;
return f[y]+(v[L[y]]-v[x]+K)%K;
}
int main()
{
freopen("t9.in","r",stdin);
freopen("t9.out","w",stdout);
scanf("%lld %lld\n",&n,&K);
fo(i,1,n)
{
scanf("%lld %lld",a+i,b+i);
s[i]=s[i-1]+a[i];
if (b[i]==1&&2*a[i]>K)
{
printf("-1\n");
return 0;
}
}
fo(i,1,n)
if (b[i]==1)
{
L[i]=((-2*s[i])%K+K)%K;
R[i]=((-2*s[i-1])%K+K)%K;
swap(L[i],R[i]);
}else L[i]=0,R[i]=K-1;
fo(i,1,n) v[++m]=L[i],v[++m]=R[i];
sort(v+1,v+m+1);
m=unique(v+1,v+m+1)-v-1;
fd(i,n,1)
{
L[i]=lower_bound(v+1,v+m+1,L[i])-v;
R[i]=lower_bound(v+1,v+m+1,R[i])-v;
f[i]=calc(L[i]);
if (L[i]>R[i])
change(1,1,m,R[i]+1,L[i]-1,i);
else change(1,1,m,1,L[i]-1,i),change(1,1,m,R[i]+1,m,i);
}
ans=f[1];
fo(i,1,m)
ans=min(ans,calc(i));
printf("%lld\n",s[n]*2+ans);
}