zoukankan      html  css  js  c++  java
  • P1315 观光公交

    传送门

    显然答案等于初始时的答案减去最多可以减少的代价

    考虑在某个路段 $[l,l+1]$ 用一次加速的影响,设 $r$ 为 $l$ 往右(不包括 $l$)第一个车要等人的站,那么所有下车的站在 $[l+1,r]$ 之间的人都可以少一秒

    设第 $i$ 个站下车 $leav[i]$ 人,那么贡献即为 $sum_{i=l+1}^{r}leav[i]$ 

    每次加速以后车到后面一段的站的时间都会减少,直到某个站从人等车变成车等人,那么此时的 $r$ 也就变成了那个站

    考虑在 $l$ 最多能用几次加速并且保证每次贡献都是一样的,显然是从 $l+1$ 到 $r-1$ 之间所有站人等车的时间最小值,再之后 $r$ 变化了所以贡献也变了(此时先不考虑 $K$ 和 $D$ 的限制)

    考虑贪心,每次都选择贡献最大的位置加速并动态维护各种东西,如何证明正确性

    发现当前所有车等人的站把路程分成了很多部分,每一次加速贡献只和加速位置到这一部分右端点离开的人数有关

    随着操作大段只会被分成更多小段,所以每次都选贡献最大的操作即可

    具体就是首先每一段互不影响,设 $x,y$ 在同一段,如果 $x$ 的贡献比 $y$ 大(即 $x<y$),操作 $x$ 或 $y$ 都最终会导致 $z$ 位置($z>=y$)首先变成车等人,那么操作 $y$ 显然还不如操作 $x$ 

    如果 $x$ 贡献比 $y$ 大,并且操作 $x$ 会导致 $z$ 位置($z<y$) 首先变成车等人,那么我们就算把 $z$ 断开了也不会影响到 $y$ 的贡献,所以每次取最优即可

    然后就是如何维护的问题了,我们要动态维护的内容有:

    当前每个位置加速的贡献,并且求全局最大值以及位置

    当前每个位置人要等车多久,并且求区间最小值以及位置

    当前每个位置往右第一个车等人的位置

    用线段树维护复杂度 $O(n log n)$,长代码警告,一堆细节警告

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    using namespace std;
    typedef long long ll;
    inline ll read()
    {
        ll x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=2e5+7;
    const ll INF=1e18;
    ll n,m,K,D[N],las[N],tim[N],Ans;
    // D就是题目的D,las[i]存每个位置最晚到达的人,tim[i]存初始公交车到达每个站的时间
    ll t[N],L[N],R[N],leav[N],num[N];
    // t[i]是游客i到达的时间,L[i]是游客i的起始站,R[i]是游客i的终点站
    // leav[i]是每个站下车的人数,num[i]维护每个位置i往右第一个人等车的站r之间([i+1,r])下车的人数
    struct dat {
        ll val,pos;
        dat(ll _val=0,ll _pos=0) { val=_val; pos=_pos; }
        inline bool operator < (const dat &tmp) const {
            return val<tmp.val;
        }
    };
    struct Segtree {
        dat mx[N<<2],mi[N<<2];//最大的贡献及位置,最小的时间差及位置
        ll nxt[N],mxt[N<<2],mit[N<<2],nxtt[N<<2];
        //每个位置往右第一个车等人的站,mx的增加标记,mi的增加标记,nxt的覆盖标记
        inline void pushup(int o)
        {
            int lc=o<<1,rc=o<<1|1;
            mx[lc]<mx[rc] ? mx[o]=mx[rc] : mx[o]=mx[lc];
            mi[rc]<mi[lc] ? mi[o]=mi[rc] : mi[o]=mi[lc];
        }
        inline void pushdown(int o,int l,int r)
        {
            if(!mxt[o]&&!mit[o]&&!nxtt[o]) return;
            int lc=o<<1,rc=o<<1|1;
            if(mxt[o])
            {
                if(l!=r) mxt[lc]+=mxt[o],mxt[rc]+=mxt[o];
                mx[o].val+=mxt[o]; mxt[o]=0;
            }
            if(mit[o])
            {
                if(l!=r) mit[lc]+=mit[o],mit[rc]+=mit[o];
                mi[o].val+=mit[o]; mit[o]=0;
            }
            if(nxtt[o])
            {
                if(l==r) nxt[l]=nxtt[o];
                else nxtt[lc]=nxtt[rc]=nxtt[o];
                nxtt[o]=0;
            }
        }
        void build(int o,int l,int r)
        {
            if(l==r) { mx[o]=dat(num[l],l); mi[o]=dat(tim[l]-las[l],l); return; }
            int mid=l+r>>1; build(o<<1,l,mid); build(o<<1|1,mid+1,r);
            pushup(o);
        }
        void change_mx(int o,int l,int r,int ql,int qr,ll v)
        {
            pushdown(o,l,r); if(l>qr||r<ql) return;
            if(l>=ql&&r<=qr) { mxt[o]=v; pushdown(o,l,r); return; }
            int mid=l+r>>1;
            change_mx(o<<1,l,mid,ql,qr,v); change_mx(o<<1|1,mid+1,r,ql,qr,v);
            pushup(o);
        }
        void change_mi(int o,int l,int r,int ql,int qr,ll v)
        {
            pushdown(o,l,r); if(l>qr||r<ql) return;
            if(l>=ql&&r<=qr) { mit[o]=v; pushdown(o,l,r); return; }
            int mid=l+r>>1;
            change_mi(o<<1,l,mid,ql,qr,v); change_mi(o<<1|1,mid+1,r,ql,qr,v);
            pushup(o);
        }
        void change_nxt(int o,int l,int r,int ql,int qr,ll v)
        {
            pushdown(o,l,r); if(l>qr||r<ql) return;
            if(l>=ql&&r<=qr) { nxtt[o]=v; pushdown(o,l,r); return; }
            int mid=l+r>>1;
            change_nxt(o<<1,l,mid,ql,qr,v); change_nxt(o<<1|1,mid+1,r,ql,qr,v);
            pushup(o);
        }
        dat query_mx(int o,int l,int r,int ql,int qr)
        {
            pushdown(o,l,r); if(l>qr||r<ql) return dat(-INF,0);
            if(l>=ql&&r<=qr) return mx[o];
            int mid=l+r>>1;
            dat res1=query_mx(o<<1,l,mid,ql,qr);
            dat res2=query_mx(o<<1|1,mid+1,r,ql,qr);
            return res1<res2 ? res2 : res1;
        }
        dat query_mi(int o,int l,int r,int ql,int qr)
        {
            pushdown(o,l,r); if(l>qr||r<ql) return dat(INF,0);
            if(l>=ql&&r<=qr) return mi[o];
            int mid=l+r>>1;
            dat res1=query_mi(o<<1,l,mid,ql,qr);
            dat res2=query_mi(o<<1|1,mid+1,r,ql,qr);
            return res1<res2 ? res1 : res2;
        }
        ll query_nxt(int o,int l,int r,int pos)
        {
            pushdown(o,l,r); if(l==r) return nxt[l];
            int mid=l+r>>1;
            ll res= pos<=mid ? query_nxt(o<<1,l,mid,pos) : query_nxt(o<<1|1,mid+1,r,pos);
            pushup(o); return res;
        }
    }T;//很长的线段树(但是不难)
    
    int main()
    {
        n=read(),m=read(),K=read();
        for(int i=1;i<n;i++) D[i]=read();
        for(int i=1;i<=m;i++)
        {
            t[i]=read(),L[i]=read(),R[i]=read();
            leav[R[i]]++; las[L[i]]=max(las[L[i]],t[i]);
        }
        for(int i=n-1;i>=1;i--) leav[i]+=leav[i+1];
        for(int i=2;i<=n;i++) tim[i]=max(tim[i-1],las[i-1])+D[i-1];
        for(int i=1;i<=m;i++) Ans+=tim[R[i]]-t[i];
        T.nxt[n]=n+1;
        for(int i=n-1;i>=1;i--)
        {
            T.nxt[i]= tim[i+1]<=las[i+1] ? i+1 : T.nxt[i+1];
            num[i]=leav[i+1]-leav[T.nxt[i]+1];
        }
        T.build(1,1,n);
        while(K)//最恶心的细节在这里
        {
            if(T.mx[1].val<=0) break;
            dat res=T.query_mx(1,1,n,1,n);//注意这里要query不要直接把 T.mx[1] 拿出来,因为可能标记还没下传(我的线段树的标记下传了才生效...)
            int l=res.pos, r=T.query_nxt(1,1,n,l);
            dat z=T.query_mi(1,1,n,l+1,r-1);
            ll mx=min( min(D[l],K) , z.val );//记得K,D的限制
            Ans-=mx*res.val; D[l]-=mx; K-=mx;
            T.change_mi(1,1,n,l+1,r-1,-mx);
            if(mx==z.val)//如果之后某个站变成车等人了,维护nxt,mx
                T.change_nxt(1,1,n,l,z.pos-1,z.pos),
                T.change_mx(1,1,n,l,z.pos-1, -(leav[z.pos+1]-leav[r+1]) );//注意区间是什么
            if(!D[l]) T.change_mx(1,1,n,l,l,-INF);//注意这里如果没法再加速了就改成最小值
        }
        printf("%lld
    ",Ans);
        return 0;
    }
  • 相关阅读:
    设计模式
    三——第二部分——第二篇论文 计划建设SQL Server镜像
    非正确使用浮点数据由项目产生BUG讨论的问题
    重写ViewPager实施单一交有关切换到这个问题,并没有缓存
    1159 Palindrome(最小插入回文串)
    CSDN-markdown编者LaTex数学公式
    找工作总结
    开展:随笔记录 OSGI的jar增加了一些小问题和注意事项
    js jquery版本号 金额千分之一转换功能(非规范,高效率)
    dom4j.jar有什么作用?
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/11580263.html
Copyright © 2011-2022 走看看