【题目】
Atcoder
题目有点复杂。
有 个车站编号为 ,以及 条轨道连接第 和第 个车站,通过它要花费 的时间。同时轨道可能是单向或双向的,双向可以同时允许两个方向列车行驶,单向在一个时间只允许一辆车在上面运行(没有车时可以改变方向)
现在要求运行从 到 的列车和从 到 的列车,满足(以下以前者为栗):
- 如果它在 时刻离开第 个站台,则会在第 的时刻到达 站台。
- 如果它在 时刻到达 站台并在 时刻离开,那么下一列车会在 时刻到达 站台并在 时刻离开。(即以 为一次循环。
- 任意时刻不能有两辆相向而行的列车在单向轨道内互相穿过。
最小化两个方向的火车从起点到终点的时间和。
【解题思路】
一看就很神的一道题。
首先我们就没什么思路,不妨考虑将所有的限制条件先写出来:
设
为正向每站等待时间,
为逆向,由于整个过程都是可以放在模
意义下去做:
走到一个点
的时间就是:
于是对于一个
的轨道限制,其运行区间就是:
逆向同理。
上面这个放在模
意义下后变成前缀和取负的操作十分需要体会。总之我们对上面的限制进行整理与化简,大概是将一个端点不能在另一个区间里作为一条不等式,这样会有两条不等式四个不等号,最终我们可以得到:
其中
为
的前缀和,这个东西无解的条件就是
。
我们最后要求的实际上是最小化 ,那么只用最小化前两个就行了,当然在这里因为柿子只与和有关,它们可以看作一个值。
有了上面的不等式,再由于我们是在模
意义下,可以把前一段补到后面去,我们的问题就变成了给定
个区间,任选起点,走
步,第
步需要落在第
个区间中,求最小总路径长度。
即形如:
然后使得
最小。
观察到每次到下一个区间的限制,我们要么留在原地不动,要么走到下一个区间的左端点位置,而如果我们走到了一个左端点,接下来的路径是一定的。不妨倒着维护这个过程,定义 表示对于当前区间 的左端点 而言,往后走到第 个区间的最小长度。假设当前我们做完了第 个区间,我们可以将 这段区间的补集覆盖成 。推 时,假设 位置上有一个值 ,说明编号在 中的所有区间都覆盖了这个点,那么我们可以原地不动直到第 个区间,此时我们再移动过去即可,转移就是
最后枚举起点算一下答案即可,复杂度 ,当然需要离散化辣。
【参考代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e5+10;
int n,K,cnt;
int a[N],b[N],c[N<<1],l[N],r[N];
ll ans,s[N],f[N];
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
struct Segment
{
#define ls (x<<1)
#define rs (x<<1|1)
int col[N<<2],tar[N<<2];
void pushdown(int x)
{
if(!tar[x]) return;
col[ls]=col[rs]=tar[ls]=tar[rs]=tar[x];tar[x]=0;
}
void update(int x,int l,int r,int L,int R,int v)
{
if(L>R) return;
if(L<=l && r<=R){col[x]=tar[x]=v;return;}
pushdown(x);
int mid=(l+r)>>1;
if(L<=mid) update(ls,l,mid,L,R,v);
if(R>mid) update(rs,mid+1,r,L,R,v);
}
int querycol(int x,int l,int r,int p)
{
if(l==r) return col[x];
pushdown(x);
int mid=(l+r)>>1;
if(p<=mid) return querycol(ls,l,mid,p);
else return querycol(rs,mid+1,r,p);
}
ll query(int x)
{
int pos=querycol(1,1,cnt,x);
return pos?f[pos]+(c[l[pos]]-c[x]+K)%K:0;
}
#undef ls
#undef rs
}tr;
int main()
{
#ifdef Durant_Lee
freopen("AGC011F.in","r",stdin);
freopen("AGC011F.out","w",stdout);
#endif
n=read();K=read();
for(int i=1;i<=n;++i)
{
a[i]=read();b[i]=read();s[i]=s[i-1]+a[i];
if(b[i]&1) l[i]=(K-s[i-1]*2%K)%K,r[i]=(K-s[i]*2%K)%K;
else l[i]=0,r[i]=K-1;
if(b[i]&1 && a[i]*2>K){puts("-1");return 0;}
c[++cnt]=l[i];c[++cnt]=r[i];
}
sort(c+1,c+cnt+1);cnt=unique(c+1,c+cnt+1)-c-1;
for(int i=1;i<=n;++i) l[i]=lower_bound(c+1,c+cnt+1,l[i])-c,r[i]=lower_bound(c+1,c+cnt+1,r[i])-c;
for(int i=n;i;--i)
{
f[i]=tr.query(l[i]);
if(l[i]>r[i]) tr.update(1,1,cnt,r[i]+1,l[i]-1,i);
else tr.update(1,1,cnt,1,l[i]-1,i),tr.update(1,1,cnt,r[i]+1,cnt,i);
}
ans=f[1];
for(int i=cnt;i;--i) ans=min(ans,tr.query(i));
printf("%lld\n",ans+2*s[n]);
return 0;
}