POJ3468 线段树||伸展树

         线段树的模板题,先给一列数,然后给一些操作,Q操作查询[l,r]区间和,C操作把区间[l,r]全部加上C。最近在学伸展树,就重新把这题做了一下。用伸展树的话,以区间中间点为下表,区间和为键值建树,左孩子记左区间,右孩子记右区间。同时为了处理方便,引入编号为0和n+1的节点。每次操作时,把节点l-1伸展到根,把r+1伸展到根节点的右孩子,这样要处理的区间就被包含在根节点和他右孩子之间了,Q操作就直接查询个和,C操作就上标记,恶心一点的就是旋转中标记的下传,不过这个仔细想想也好理解。

上面是伸展树写的下面是原来用线段树写的,不得不说这题还是线段树写比较合适..代码短跑得还快...而且写伸展树的时候用C++交莫名其妙的WA&TLE了一下午,换G++竟然擦边过了,好不科学-

下面上代码,先是伸展树的

#include <iostream>
#include <cstdio>
#include <cstring>

typedef long long ll;
using namespace std;
const int maxn=100000+6;
int pre[maxn],ch[maxn][2];
ll key[maxn];
int add[maxn];
int size[maxn];
ll val[maxn];
int a[maxn];
int root;
int n,m;
int l,r;
void pushup(int r)
{
    key[r]=key[ch[r][0]]+key[ch[r][1]]+(ll)val[r]+(ll)add[r];
    size[r]=size[ch[r][0]]+size[ch[r][1]]+1;
}
void pushdown(int r)
{
    int v;
    if (add[r])
    {
        val[r]+=add[r];
        if (ch[r][0]!=-1)
        {
          v=ch[r][0];
          add[v]+=add[r];
          key[v]+=((ll)add[r]*(ll)size[v]);
        }
        if (ch[r][1]!=-1)
        {
            v=ch[r][1];

            add[v]+=add[r];
            key[v]+=((ll)add[r]*(ll)size[v]);
        }
        add[r]=0;
        pushup(r);
    }
}
void build(int l,int r,int id)
{
    if (id==-1) root=(l+r)>>1;
    if (l==r)
    {
        key[l]=(ll)a[l];
        size[l]=1;
        pre[l]=id;
        ch[l][0]=ch[l][1]=-1;
    }
    else
    {
        int m=(l+r)>>1;
        pre[m]=id;
        if (m>l)ch[m][0]=(l+m-1)>>1,build(l,m-1,m); else ch[m][0]=-1;
        if (m<r)ch[m][1]=(m+1+r)>>1,build(m+1,r,m); else ch[m][1]=-1;
        pushup(m);
    }
}
void rotate(int x,int kind)
{
    int y=pre[x];
    pushdown(y);
    pushdown(x);
    ch[y][!kind]=ch[x][kind];
    pre[ch[x][kind]]=y;
    if (pre[y]!=-1)
    {
        ch[pre[y]][ch[pre[y]][1]==y]=x;
    }
    pre[x]=pre[y];
    ch[x][kind]=y;
    pre[y]=x;
    pushup(y);
    pushup(x);
}
void splay(int r,int tgt)
{
    while(pre[r]!=tgt)
    {
        if(pre[pre[r]]==tgt)
        {
            rotate(r,ch[pre[r]][0]==r);
        }
        else
        {
            int y=pre[r];
            int kind=ch[pre[y]][0]==y;
            if (ch[y][kind]==r)
            {
                rotate(r,!kind);
                rotate(r,kind);
            }
            else
            {
                rotate(y,kind);
                rotate(r,kind);
            }
        }
    }
    if (tgt==root) pushup(root);
    if (tgt==-1) root=r;
}

ll query(int r)
{
    pushdown(r);
    return key[r];
}
int main()
{
//    freopen("in.txt","r",stdin);
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(add,0,sizeof add);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            val[i]=a[i];
        }
        root=-1;
        build(0,n+1,root);
//        print(root);
//        cout<<endl;
        int num;
        char type[10];
        for (int i=1;i<=m;i++)
        {
            scanf("%s%d%d",type,&l,&r);
            splay(l-1,-1);
            splay(r+1,root);
            if (type[0]=='C')
            {
                scanf("%d",&num);
                add[ch[ch[root][1]][0]]+=num;
                key[ch[ch[root][1]][0]]+=((ll)size[ch[ch[root][1]][0]]*(ll)num);
            }
            else
            {
                printf("%lld\n",query(ch[ch[root][1]][0]));
            }
        }
    }
    return 0;
}

线段树的

include <iostream>
#include <algorithm>
#include <cstdio>
#include <memory.h>
#include <cmath>
#include <string>
using namespace std;

#define lson id<<1,l,m
#define rson id<<1|1,m+1,r
#define ll long long
const int maxn=100000+10;
ll sum[maxn<<2];
ll a[maxn];
int add[maxn<<2];
int n,m;

void pushup(int id)
{
    sum[id]=sum[id<<1]+sum[id<<1|1];
}
void pushdown(int id,int m)
{
    if (add[id])
    {
        //add[id<<1]=add[id<<1|1]=add[id];
        add[id<<1]+=add[id];
        add[id<<1|1]+=add[id];
        sum[id<<1]=sum[id<<1]+(ll)add[id]*(m-(m>>1));
        sum[id<<1|1]=sum[id<<1|1]+(ll)add[id]*(m>>1);
        add[id]=0;
    }
}
void build(int id,int l,int r)
{
    add[id]=0;
    if (l==r)
    {
        scanf("%lld",&sum[id]);
        return;
    }
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    pushup(id);
}
void updata(int L,int R,int c,int id,int l,int r)
{
    if (L<=l && r<=R)
    {
        add[id]+=c;
        sum[id]+=((ll)c*(r-l+1));
        return ;
    }
    pushdown(id,r-l+1);
    int m=(l+r)>>1;
    if (L<=m) updata(L,R,c,lson);
    if (R>m) updata(L,R,c,rson);
    pushup(id);
}
ll query(int L,int R,int id,int l,int r)
{
    if (L<=l && r<=R)
    {
        return sum[id];
    }
    pushdown(id,r-l+1);
    ll ret=0;
    int m=(l+r)>>1;
    if (L<=m) ret+=query(L,R,lson);
    if (R>m) ret+=query(L,R,rson);
    return ret;
}

int main()
{
    //freopen("a.in","r",stdin);

    while (~scanf("%d%d",&n,&m))
    {
        build(1,1,n);
        char op[4];
        int a,b,c;
        for (int i=1; i<=m; i++)
        {
        scanf("%s",op);
        if (op[0]=='Q')
        {
            scanf("%d%d",&a,&b);
            printf("%lld\n",query(a,b,1,1,n));
        }
        else
        {
            scanf("%d%d%d",&a,&b,&c);
            updata(a,b,c,1,1,n);
        }
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值