SPOJ GSS3 - Can you answer these queries III【线段树 or Splay】

题目描述

You are given a sequence A of N (N <= 50000) integers between -10000 and 10000. On this sequence you have to apply M (M <= 50000) operations:
modify the i-th element in the sequence or for given x y print max{Ai + Ai+1 + .. + Aj | x<=i<=j<=y }.
$n $个数,$ q $次操作
操作0 x y把 $A_x$ 修改为 y
操作1 l r询问区间$ [l, r] $的最大子段和

输入格式:

The first line of input contains an integer N. The following line contains N integers, representing the sequence A1..AN.
The third line contains an integer M. The next M lines contain the operations in following form:
0 x y: modify Ax into y (|y|<=10000).
1 x y: print max{Ai + Ai+1 + .. + Aj | x<=i<=j<=y }.

输出格式:

For each query, print an integer as the problem required.

输入样例

4
1 2 3 4
4
1 1 3
0 3 -3
1 2 4
1 3 3

输出样例:

6
4
-3
******************************

题目分析:

lx[]数组维护区间内从以区间最左端元素为第一个数的最大连续子段和
rx[]数组维护区间内从以区间最右端元素为最后一个数的最大连续子段和
mx[]数组维护区间内最大连续子段和
那么维护的代码如下

void update(int p)//这里是splay写法,线段树lc=p<<1,rc=p<<1|1
{
    int lc=ch[p][0],rc=ch[p][1];
    size[p]=size[lc]+size[rc]+1;
    sum[p]=sum[lc]+sum[rc]+val[p];
    lx[p]=max(lx[lc],sum[lc]+lx[rc]+val[p]);
    rx[p]=max(rx[rc],sum[rc]+rx[lc]+val[p]);
    mx[p]=max(max(mx[lc],mx[rc]),rx[lc]+lx[rc]+val[p]);
}

lx[p]=max(lx[左子树], sum[左子树]+lx[右子树]+val[p]);

rx[p]=max(rx[右子树], sum[右子树]+rx[左子树]+val[p]);

mx[p]=max(mx[左子树], mx[右子树], rx[左子树]+lx[右子树]+val[p]);


给出splay的完整代码
线段树的话有空再填上

#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

const int maxn=100010;
int n,m;
int rt;
int val[maxn],sum[maxn];
int ch[maxn][2],fa[maxn];
int size[maxn];
int lx[maxn],rx[maxn],mx[maxn];

void update(int p)
{
    int lc=ch[p][0],rc=ch[p][1];
    size[p]=size[lc]+size[rc]+1;
    sum[p]=sum[lc]+sum[rc]+val[p];
    lx[p]=max(lx[lc],sum[lc]+lx[rc]+val[p]);
    rx[p]=max(rx[rc],sum[rc]+rx[lc]+val[p]);
    mx[p]=max(max(mx[lc],mx[rc]),rx[lc]+lx[rc]+val[p]);
}

void build(int p,int ll,int rr)
{
    if(ll>rr) return;
    int mid=ll+rr>>1;
    fa[mid]=p; size[mid]=1;
    ch[p][mid>p]=mid;
    if(ll==rr) 
    { 
        lx[mid]=rx[mid]=max(val[mid],0); //注意要判断负数
        sum[mid]=mx[mid]=val[mid];  return;
    }
    build(mid,ll,mid-1); build(mid,mid+1,rr);
    update(mid);
}

void rotate(int &p,int x)
{
    int y=fa[x],z=fa[y];
    int d=(ch[y][0]==x);
    if(y==p) p=x;
    else if(ch[z][0]==y) ch[z][0]=x;
    else ch[z][1]=x;
    fa[y]=x; fa[ch[x][d]]=y; fa[x]=z;
    ch[y][d^1]=ch[x][d]; ch[x][d]=y;
    update(y); update(x);
}


void splay(int &p,int x)
{
    while(x!=p)
    {
        int y=fa[x],z=fa[y];
        if(y!=p)
        {
            if((ch[z][0]==y)^(ch[y][0]==x))rotate(p,x);
            else rotate(p,y);
        }
        rotate(p,x);
    }
}

int find(int p,int k) 
{ 
    int ss=size[ch[p][0]]; 
    if(k==ss+1) return p;    
    else if(k<=ss) return find(ch[p][0],k);
    else return find(ch[p][1],k-ss-1);  
}

void change(int k,int w)
{
    int x=find(rt,k); splay(rt,x);
    int y=find(rt,k+2); splay(ch[rt][1],y);
    int z=ch[y][0];//由于伸展出的区间只有一个点,所以直接修改
    val[z]=mx[z]=sum[z]=w;
    lx[z]=rx[z]=max(w,0);
    update(y); update(x);
}

void query(int ll,int rr)//直接伸展出对应区间,输出其mx
{
    int x=find(rt,ll); splay(rt,x);
    int y=find(rt,rr+2); splay(ch[rt][1],y);
    printf("%d\n",mx[ch[y][0]]);
}

int main()
{
    n=read();
    mx[0]=val[1]=val[n+2]=-1e9;//哨兵结点的值记得初始化-inf
    for(int i=2;i<=n+1;++i)
    val[i]=read();
    
    rt=n+3>>1;
    build(rt,1,n+2);
    
    m=read();
    while(m--)
    {
        int k=read(),x=read(),y=read();
        if(k==0) change(x,y);
        else if(k==1) query(x,y);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/niiick/p/8946315.html
今日推荐