peiwenjun's blog 没有知识的荒原

CF453E Little Pony and Lord Tirek 题解

题目描述

维护长为 \(n\) 的序列 \(s\) ,初始值( \(0\) 单位时间)给定且 \(0\le s_i\le m_i\)

每单位时间, \(\forall i\in[1,n]\) ,执行 \(s_i=\min(m_i,s_i+r_i)\)

接下来 \(q\) 次操作,给定 \(t_i,l,r\) ,求第 \(t_i\) 单位时间的 \(\sum_{i=l}^rs_i\) ,然后将区间内的 \(s_i\) 清零。

数据范围

  • \(1\le n,q\le10^5\)
  • \(0\le s_i\le m_i\le 10^5,0\le r_i\le 10^5\)
  • \(0\le t_i\le 10^9,1\le l\le r\le n\) ,保证 \(t_i\) 严格递增。

时间限制 \(\texttt{3s}\) ,空间限制 \(\texttt{256MB}\)

分析

先不考虑清空,假设初始值全为 \(0\) ,如何求 \(t_i\) 时间的 \(\sum_{i=l}^rs_i\)

显然 \(s_i=\min(m_i,t\cdot r_i)\) ,这启示我们需要分类讨论。

如果 \(\lfloor\frac{m_i}{r_i}\rfloor\le t\) ,那么贡献为 \(m_i\) ,否则为 \(t\cdot r_i\)

\((m_i,r_i)\) 放在值域 \(\lfloor\frac{m_i}{r_i}\rfloor\) 的位置,主席树维护 \(m_i\) 的前缀和、 \(r_i\) 的后缀和即可。


再来考虑区间推平如何处理。

考虑颜色段均摊。每个连续段用三元组 \((l,r,t)\) 表示,并存储在 set 中。

区间推平时先通过 split 操作将 \(l\)\(r+1\) 分裂成连续段开头,删除构成 \([l,r]\) 的若干个连续段,再加入 \([l,r]\) 连续段。

由于每个连续段至多被删除一次,并且单次操作只会加入和分裂 \(\mathcal O(1)\) 个连续段,因此连续段总数为 \(\mathcal O(n+q)\)

计算连续段的贡献只需将 \(t_i\) 减去连续段的插入时间,然后变成上面的问题。

由于 \(s_i\) 有初始值,所以还要特判未被清空的情况。

另外 \(r_i=0\) 的位置不必插入主席树,否则可能会因为除数为 \(0\) 而导致 \(\texttt{RE}\)

时间复杂度 \(\mathcal O((n+q)\log(n+q))\)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int lim=1e5,maxn=1e5+5;
int n,q,t,L,R,tot;
ll res;
int m[maxn],r[maxn],v[maxn],rt[maxn];
struct line
{
    int l,r,t;
    bool operator<(const line&p)const
    {
        if(l!=p.l) return l<p.l;
        return r<p.r;
    }
};
set<line> s;
void split(int x)
{
    if(x==n+1) return ;
    auto now=*--s.upper_bound({x,lim,0});
    if(now.l==x) return ;
    assert(now.l<x&&x<=now.r);
    s.erase(now),s.insert({now.l,x-1,now.t}),s.insert({x,now.r,now.t});
}
struct node
{
    int ls,rs;
    ll sum[2];
}f[20*maxn];
void pushup(int p)
{
    f[p].sum[0]=f[f[p].ls].sum[0]+f[f[p].rs].sum[0];
    f[p].sum[1]=f[f[p].ls].sum[1]+f[f[p].rs].sum[1];
}
int insert(int p,int l,int r,int pos,int v1,int v2)
{
    int q=++tot;
    f[q]=f[p];
    if(l==r)
    {
        f[q].sum[0]+=v1,f[q].sum[1]+=v2;
        return q;
    }
    int mid=(l+r)/2;
    if(pos<=mid) f[q].ls=insert(f[q].ls,l,mid,pos,v1,v2);
    else f[q].rs=insert(f[q].rs,mid+1,r,pos,v1,v2);
    pushup(q);
    return q;
}
ll query(int p,int q,int l,int r,int L,int R,int op)
{
    if(L<=l&&r<=R) return f[q].sum[op]-f[p].sum[op];
    if(L>r||R<l) return 0;
    int mid=(l+r)/2;
    return query(f[p].ls,f[q].ls,l,mid,L,R,op)+query(f[p].rs,f[q].rs,mid+1,r,L,R,op);
}
ll calc(int l,int r,int t)
{
    return query(rt[l-1],rt[r],0,lim,0,t-1,0)+t*query(rt[l-1],rt[r],0,lim,t,lim,1);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%d",&v[i],&m[i],&r[i]);
        s.insert({i,i,-1});
        if(!r[i]) rt[i]=rt[i-1];
        else rt[i]=insert(rt[i-1],0,lim,m[i]/r[i],m[i],r[i]);
    }
    scanf("%d",&q);
    while(q--)
    {
        scanf("%d%d%d",&t,&L,&R),res=0;
        split(L),split(R+1);
        for(auto it=s.lower_bound({L,0,0});it!=s.end()&&(*it).l<=R;it=s.erase(it))
        {
            if((*it).t==-1)
            {
                int x=(*it).l;
                res+=min(v[x]+1ll*t*r[x],(ll)m[x]);
            }
            else res+=calc((*it).l,(*it).r,t-(*it).t);
        }
        s.insert({L,R,t});
        printf("%lld\n",res);
    }
    return 0;
}

posted on 2022-07-09 15:59  peiwenjun  阅读(12)  评论(0)    收藏  举报

导航