[agc011f]Train Service Planning

题目大意

有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..m1a[i]+i=0..m1p[i],i=0..ma[i]+i=0..m1p[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..ma[i]+i=0..m1p[i],i=0..m1a[i]+i=0..m1p[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])(2s[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意义下补集转化就变成每个 msum[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);
}

你可能感兴趣的:(构造,线段树,树状数组)