【AtCoder】【AGC011f】Train Service Planning

Description

有一条地铁线路,n段路,n+1个站,每段路可以同时交汇走两辆车或只能走一辆车,
每个站可以停车,停车不占用线路,
现在每隔Kmin发一次车,分别从起点/终点开向终点/起点,
每段路要走的时间已知,求两个方向的辆车运行时间总和最小

Soluton

因为是每隔Km分钟发一次车,因此考虑在mod K的情况下做,
先把双向的线路忽略,只考虑单向的,
设第i段路要走 Ai 分钟,第i站的停站时间分别为 qi,pi
两个方向的车不会相交,当且仅当:
(q0...i1+A1..i1,q0...i1+A1..i)
与区间
(pi...n+Ai..n,pi...n+Ai1..n)
没有交点,
把第二个式子换个方式写:
(Np0...i1A1..i1,Np0...i1A1..i)

显然,式子是在mod意义下的,同时也可以通过调节使得N为K的倍数,所以整理后即为:
(q0...i1+A1..i1,q0...i1+A1..i)
与区间
(p0...i1A1..i1,p0...i1A1..i)
没有交点,
先做q+p的前缀和,设为 Si
判断区间相交可以用判断端点是否被包含,写出不等式后发现,我们只要保证
q0...i1+p0...i1 不在区间 (2A1..i1,2A1..i) 内即可,

也就是,在数轴上走,要保证第i个时刻不在某个区间内,
我们的目的是使 SnS0 最小,(这个不取模)
发现每次如果要走,肯定是走到左端点上,

这个直接用线段树做即可,每次查询一个区间最小值,再区间更改权值,

复杂度: O(nlog(n))

Code

#include 
#include 
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define min(q,w) ((q)>(w)?(w):(q))
#define max(q,w) ((q)<(w)?(w):(q))
using namespace std;
typedef long long LL;
const int N=100500;
const LL INF=1e17;
int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int m,n,n1;
LL a[N],ans;
bool az[N];
int L[N],R[N];
struct Data
{
    int l,r;
    LL v,la;
}b[N*100];
int root,b0;
void doit(int l,int r,int e,int K=1)
{
    if(!e||b[e].la<0)return;
    b[e].v=b[e].la;
    if(l!=r)
    {
        if(b[e].l||K)b[b[e].l?b[e].l:(b[e].l=++b0)].la=b[e].la;
        if(b[e].r||K)b[b[e].r?b[e].r:(b[e].r=++b0)].la=b[e].la;
    }
    b[e].la=-1;
}
void change(int l,int r,int &e,int l1,int r1,LL l2)
{
    if(l>r||l1>r1)return;
    if(!e)e=++b0;
    if(l1<=l&&r<=r1)
    {
        b[e].la=l2;
        doit(l,r,e);
        return;
    }
    int t=(l+r)>>1;
    doit(l,t,b[e].l),doit(t+1,r,b[e].r);
    if(l1<=t)change(l,t,b[e].l,l1,r1,l2);
    if(t1,r,b[e].r,l1,r1,l2);
    b[e].v=min(b[b[e].l].v-(LL)t+r,b[b[e].r].v);
}
LL find(int l,int r,int e,int l1,int r1)
{
    if(!e||rr1)return INF;
    doit(l,r,e);
    if(l1<=l&&r<=r1)return b[e].v-(LL)r+r1;
    int t=(l+r)>>1;
    LL ans=INF,ans1=INF;
    if(l1<=t)ans=find(l,t,b[e].l,l1,r1);
    if(t1,r,b[e].r,l1,r1);
    return min(ans,ans1);
}
LL findS(int l,int r,int e)
{
    if(!e)return INF;
    doit(l,r,e,0);
    if(l==r)return b[e].v;
    int t=(l+r)>>1;
    LL ans=INF,ans1=INF;
    ans=findS(l,t,b[e].l);
    ans1=findS(t+1,r,b[e].r);
    return min(ans,ans1);
}
int main()
{
    int q,w;
    read(n),read(n1);
    fo(i,1,n)
    {
        a[i]=a[i-1]+(LL)read(q);az[i]=read(w)-1;
        if(q*2>n1&&w==1)return printf("-1\n"),0;
        if(w==1)L[i]=(n1-2LL*a[i]%n1+1)%n1,R[i]=(n1-2LL*a[i-1]%n1-1)%n1;
    }
    b0=1;b[0].v=INF,b[1].v=0;
    b[0].la=-1,b[1].la=0;
    root=1;
    fo(i,1,n)if(!az[i])
    {
        if(L[i]<=(R[i]+1)%n1) 
        {
            LL t=find(0,n1-1,root,L[i],(R[i]+1)%n1);
            change(0,n1-1,root,L[i],R[i],INF);
            change(0,n1-1,root,(R[i]+1)%n1,(R[i]+1)%n1,t);
        }
        else
        {
            LL t=find(0,n1-1,root,L[i],n1-1)+(R[i]+1LL)%n1+1LL;
            change(0,n1-1,root,L[i],n1-1,INF);
            LL t1=find(0,n1-1,root,0,(R[i]+1)%n1);
            change(0,n1-1,root,0,(R[i]+1)%n1,INF);
            change(0,n1-1,root,(R[i]+1)%n1,(R[i]+1)%n1,min(t,t1));
        }
    }
    ans=findS(0,n1-1,root)+a[n]*2LL;
    printf("%lld\n",ans);
    return 0;
}

你可能感兴趣的:(妙啊)