UOJ Logo Ignatz的博客

博客

spoj UNTITLE1

2016-02-12 22:41:05 By Ignatz

$spoj\ UNTITLE1$

题目大意

维护序列$\{a\}$, $|a_i|\leq 10^4$; 记$s_i=\sum_{j=1}^i a_j$

有两种操作

$0\ x\ y\ k$: 将$a_i$增加$k\ (x\leq i\leq y,\ |k|\leq 10^4)$

$1\ x\ y$: 询问$max(s_i\ |\ x\leq i\leq y)$

定义: $f(x)$表示$x$所在的块的序号

各种题解都是对$\{s\}$分块呐(据说可持久化平衡树也可以,然而我找不到$clj$的课件$orz$)

修改:

$$ s_i\rightarrow \left\{ \begin{array}{ll} s_i+k(i-x+1)=s_i+ki+k(1-x) & \textrm{$x\leq i\leq y$} \\ s_i+k(y-x+1) & \textrm{$y< i$} \end{array} \right. $$

$k(1-x),\ k(y-x+1)$为常数

对整块修改时, 所修改的常数是相等的, 用$\{c\}$记录; 用$\{k\}$记录每块共同增加的$k$

则询问时输出$max(s_i+k_{f(i)}i+c_{f(i)}\ |\ x\leq i\leq y)$

$\therefore k,\ c$是常数, 这个式子和斜率优化$dp$的状态转移方程类似

$\therefore$每次暴力修改后维护块中由所有$(i,s_i)$所构成的点集的上凸形; 每次对整块的询问, 对上凸形二分答案即可

Code

$ps$: $[0,n),\ [0,tot],\ [ql,qr),\ [l,r),\ [bl,br],\ rep(i,x,y)\Leftrightarrow For\ i\in[x,y)$

#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cmath>
#define rep(i,x,y) for (int i=x; i<y; ++i)

using namespace std;
typedef long long ll;

int n,m,op,ql,qr,v,tot,st[225][225],sz,bl,br;
ll k[225],c[225],s[50000],ans;

inline int get()
{
    char ch;
    int ret;
    bool f;
    while (!isdigit(ch=getchar()) && ch!=45);
    for (ch==45? (ret=0,f=1):(ret=ch-48,f=0); isdigit(ch=getchar()); ret=ret*10+ch-48);
    return f? -ret:ret;
}

inline ll query(int x,int l=0,int r=0)
{
    if (l%sz || r%sz)
    {
        ll ret=s[l]+l*k[x];
        rep(i,l+1,r)
            ret=max(ret,s[i]+i*k[x]);
        return ret+c[x];
    }
    int *a=st[x],mid,t,t_;
    if (!*a)
        rep(i,x*sz,(x+1)*sz)
        {
            while (t=a[*a],t_=a[*a-1],*a>1 && (s[i]-s[t])*(t-t_)>(s[t]-s[t_])*(i-t))
                --*a;
            a[++*a]=i;
        }
    for (l=1,r=*a-1; l<=r;)
    {
        mid=l+r>>1,t=a[mid+1],t_=a[mid];
        if (s[t]-s[t_]>-k[x]*(t-t_))
            l=mid+1;
        else
            r=mid-1;
    }
    return s[a[l]]+a[l]*k[x]+c[x];
}

inline void modify(int x,int l=0,int r=0)
{
    if (l%sz || r%sz)
    {
        *st[x]=0;
        for (ll i=l,j=(l-ql+1)*v; i<r; ++i,j+=v)
            s[i]+=j;
        return;
    }
    k[x]+=v,c[x]+=(1-ql)*v;
}

int main()
{
    n=get(),sz=sqrt(n),tot=(n-1)/sz,s[0]=get();
    rep(i,1,n)
        s[i]=get()+s[i-1];
    for (int q=get(); q--;)
    {
        op=get(),ql=get()-1,qr=get(),bl=ql/sz,br=(qr-1)/sz;
        if (op)
        {
            if (bl==br)
                ans=query(bl,ql,qr);
            else
                ans=max(query(bl,ql,(bl+1)*sz),query(br,br*sz,qr));
            rep(i,bl+1,br)
                ans=max(ans,query(i));
            printf("%lld\n",ans);
        }
        else
        {
            if (v=get(),bl==br)
                modify(bl,ql,qr);
            else
                modify(bl,ql,(bl+1)*sz),modify(br,br*sz,qr);
            rep(i,bl+1,br)
                modify(i);
            v*=qr-ql;
            rep(i,qr,min(n,(br+1)*sz))
                s[i]+=v;
            rep(i,br+1,tot+1)
                c[i]+=v;
        }
    }
    return 0;
}

评论

zhouzixuan
%%%orz
wyh2000
伏地%神犇

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。