BZOJ3188. [Coci 2011]Upit

传送门

有插入的操作,而且两种修改对区间和都可以打标记,所以直接平衡树维护就完事了

区间加等差数列的操作就维护一个 $s,p$ 表示区间加以 $s$ 为首项,$p$ 为公差的数列,然后记得覆盖标记要直接把以前的标记都覆盖

因为我不知道区间覆盖的值域是多少,所以多维护一个 $pd$ 判断是否有覆盖标记

记得区间加等差数列的时候标记给右儿子的 $s$ 要变大

下传标记我是在每次 $splay$ 之前先把整条链的标记传下来,打标记是把 $l-1$ 移到根,$r+1$ 移到根的右儿子

标记打完要把根和根的右儿子的数据更新,因为会访问到 $0$ 和 $n+1$ 所以要多两个虚节点防止越界

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
inline int read()
{
    int 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;
int n,m,a[N];
int rt,c[N][2],fa[N],sz[N],cnt;
ll val[N],sum[N];
struct dat {
    ll c,s,p,pd;
    dat (ll _c=0,ll _s=0,ll _p=0,ll _pd=0) { c=_c,s=_s,p=_p,pd=_pd; }
    inline ll calc(int n) { return pd*c*n+s*n+1ll*n*(n-1)/2*p; }
    inline ll val(int n) { return pd*c+s+1ll*n*p; }
    inline dat operator + (const dat &tmp) const {
        if(tmp.pd) return dat(tmp.c,tmp.s,tmp.p,tmp.pd);
        return dat(c,s+tmp.s,p+tmp.p,pd);
    }
}tag[N];
inline void pushup(int x)
{
    int &lc=c[x][0],&rc=c[x][1];
    sz[x]=sz[lc]+sz[rc]+1;
    sum[x]=sum[lc]+sum[rc]+val[x];
}
inline void pushdown(int x)
{
    if(!x||(!tag[x].s&&!tag[x].p&&!tag[x].pd)) return;
    int &lc=c[x][0],&rc=c[x][1];
    if(tag[x].pd) sum[x]=0,val[x]=0;
    sum[x]+=tag[x].calc(sz[x]); val[x]+=tag[x].val(sz[c[x][0]]);
    if(lc) tag[lc]=tag[lc]+tag[x];
    tag[x].s+=tag[x].p*(sz[c[x][0]]+1);//
    if(rc) tag[rc]=tag[rc]+tag[x];
    tag[x]=dat(0,0,0,0);
}
inline void rotate(int x,int &k)
{
    int y=fa[x],z=fa[y],d=(c[y][1]==x);
    y!=k ? c[z][c[z][1]==y]=x : k=x;
    fa[x]=z; fa[y]=x; fa[c[x][d^1]]=y;
    c[y][d]=c[x][d^1]; c[x][d^1]=y;
    pushup(y); pushup(x);
}
void push_tag(int x)
{
    if(x!=rt) push_tag(fa[x]);
    else pushdown(x);
    pushdown(c[x][0]); pushdown(c[x][1]);
}
inline void splay(int x,int &k)
{
    push_tag(x);
    while(x!=k)
    {
        int y=fa[x],z=fa[y];
        if(y!=k)
            (c[y][0]==x ^ c[z][0]==y) ? rotate(x,k) : rotate(y,k);
        rotate(x,k);
    }
}
inline int K_th(int k)
{
    int x=rt;
    while(233)
    {
        if(sz[c[x][0]]+1==k) return x;
        if(sz[c[x][0]]>=k) x=c[x][0];
        else k-=sz[c[x][0]]+1,x=c[x][1];
    }
}
inline void split(int l,int r) { splay(K_th(l-1),rt); splay(K_th(r+1),c[rt][1]); }
inline void Cover(int l,int r,ll v)
{
    split(l,r); int x=c[c[rt][1]][0];
    tag[x]=tag[x]+dat(v,0,0,1); pushdown(x);
    pushup(fa[x]); pushup(rt);
}
inline void Add(int l,int r,ll p)
{
    split(l,r); int x=c[c[rt][1]][0];
    tag[x]=tag[x]+dat(0,p,p,0); pushdown(x);
    pushup(fa[x]); pushup(rt);
}
inline void Ins(int k,ll v)
{
    splay(K_th(k),rt); int f=K_th(k+1); splay(f,c[rt][1]);
    int &x=c[f][0]; x=++cnt;
    fa[x]=f; sz[x]=1; sum[x]=val[x]=v;
    pushup(f); pushup(rt);
}
inline void Query(int l,int r)
{
    split(l,r); int x=c[c[rt][1]][0]; pushdown(x);
    printf("%lld\n",sum[x]);
}
void build(int l,int r,int f)
{
    int mid=l+r>>1;
    if(l<mid) build(l,mid-1,mid);
    if(mid<r) build(mid+1,r,mid);
    val[mid]=a[mid]; c[f][mid>f]=mid;
    fa[mid]=f; pushup(mid);
}
int main()
{
    n=read(),m=read();
    for(int i=2;i<=n+1;i++) a[i]=read();
    cnt=n+2; build(1,n+2,0); rt=(n+3)>>1;
    int opt,a,b;
    for(int i=1;i<=m;i++)
    {
        opt=read(),a=read(),b=read();
        if(opt==1) { Cover(a+1,b+1,read()); continue; }
        if(opt==2) { Add(a+1,b+1,read()); continue; }
        if(opt==3) { Ins(a,b); continue; }
        Query(a+1,b+1);
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/LLTYYC/p/11516196.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值