NOI2005 维修数列(综合包括求区间的最大值)---Splay

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string.h>
#include <queue>
#define N 500010     //数组要定大点
#define inf 1<<30
using namespace std;
int pre[N], key[N], ch[N][2], root, tot, rev[N], same[N], size[N], sum1[N]; //rev表示旋转标记,same表示数值改变的标记
int n, m, max1, s1, a[N], lmax[N], rmax[N], mmax[N], s[N], tot2;  //lmax指从左边开始最大值, mmax指这个区间连续的最大值, 数组s用来回收空间
/*
区间最大和,可能是左子树的最大和,右子树的最大和,左子树的右端+节点+右子树的左端
伸展树里的节点所表示的树可以看做是一个区间,最左边和最右边分别是区间的两个端点
*/
void update(int x)
{
	int j=ch[x][0], k=ch[x][1];
    size[x]=size[j]+size[k]+1;
	sum1[x]=sum1[j]+sum1[k]+key[x];
	lmax[x]=max(lmax[j], sum1[j]+key[x]+max(0, lmax[k]));  //在这个区间内
	rmax[x]=max(rmax[k], sum1[k]+key[x]+max(0, rmax[j]));
	mmax[x]=max(0, rmax[j])+key[x]+max(0, lmax[k]);
	mmax[x]=max(mmax[x], max(mmax[j], mmax[k]));
    return ;
}
void updaterev(int x) //先处理x节点,rev[x]为1表示这个节点已经处理了(旋转)  
{
	if(!x)return ;
	swap(ch[x][0], ch[x][1]);
	swap(lmax[x], rmax[x]); //翻转时左右区间也交换!
    rev[x]^=1;         
	return ;
}
void updatesame(int x, int k)
{
	if(!x)return ;
	key[x]=k;
	sum1[x]=size[x]*k;
	lmax[x]=rmax[x]=mmax[x]=max(k, k*size[x]);    //判断正负的最大值
	same[x]=1;
	return ;
}


void pushdown(int x)
{
    if(rev[x])
    {
		updaterev(ch[x][0]);
		updaterev(ch[x][1]);
		rev[x]=0;
	}    
    if(same[x])
    {
		updatesame(ch[x][0], key[x]);
		updatesame(ch[x][1], key[x]);
		same[x]=0;
    }
    return ;
}
void newnode(int &r, int father, int k)
{
	if(tot2)  //用数组s回收来的空间新建,否则会TLE
		r=s[tot2--];
    else r=++tot;
    pre[r]=father;
    size[r]=1;
    same[r]=rev[r]=0;
    ch[r][0]=ch[r][1]=0;
	key[r]=lmax[r]=rmax[r]=mmax[r]=sum1[r]=k;
	return ;
}
void build(int l, int r, int &k, int father)
{
    if(l>r)return ;
    int mid=(l+r)>>1;
    newnode(k, father, a[mid]);
    build(l, mid-1, ch[k][0], k);
    build(mid+1, r, ch[k][1], k);
    update(k);
    return ;
}
void init()
{
    pre[0]=rev[0]=same[0]=key[0]=ch[0][0]=ch[0][1]=root=tot=tot2=size[0]=0;
	lmax[0]=rmax[0]=mmax[0]=-inf;  //这里要注意初始化
    newnode(root, 0, -1);
    newnode(ch[root][1], root, -1);
    update(root);
    build(1, n, ch[ch[root][1]][0], ch[root][1]);
    update(ch[root][1]);
    update(root);
    return ;
}
void rotate(int x, int kind)
{
    int k=pre[x];
    pushdown(k);
    pushdown(x);
    ch[k][!kind]=ch[x][kind];
    pre[ch[x][kind]]=k;
    ch[x][kind]=k;
    if(pre[k])
        ch[pre[k]][ch[pre[k]][1]==k]=x;
    pre[x]=pre[k];
    pre[k]=x;
    update(k);
    return ;
}
void Splay(int x, int goal)
{
    int k, g, h;
    pushdown(x);
    while(pre[x]!=goal)
    {
        k=pre[x];
        if(pre[k]==goal)
        {
            rotate(x, ch[k][0]==x);
        }
        else
        {
            g=(ch[k][0]==x?0:1);
            h=(ch[pre[k]][0]==k?0:1);
            if(g==h)
            {
                rotate(k, !g);
                rotate(x, !g);
            }
            else
            {
                rotate(x, !g);
                rotate(x, g);
            }
        }
    }
    if(!goal)root=x;
    update(x);
    return ;
}
int select(int k, int x)
{
    pushdown(x);
    if(size[ch[x][0]]+1==k)
        return x;
    else if(size[ch[x][0]]+1>k)
        return select(k, ch[x][0]);
    else return select(k-size[ch[x][0]]-1, ch[x][1]);
}
int getmin(int r)
{
    int s=r;
    pushdown(r);
    while(r)
    {
        s=r;
        r=ch[r][0];
        pushdown(r);
    }
    return s;
}
void insert(int k, int num)
{
    int j, g;
    j=select(k+1, root);
    Splay(j, 0);
    g=getmin(ch[root][1]);
    Splay(g, root);
    build(1, num, ch[g][0], g);
    update(g);
    update(j);
    return ;
}
void erase(int r)  //回收空间
{
	if(!r)return ;
	s[++tot2]=r;
	erase(ch[r][0]);
	erase(ch[r][1]);
	return ;
}

void dele(int k, int num)
{
    int j, g;
    j=select(k, root);
    g=select(k+num+1, root);
    Splay(j, 0);
    Splay(g, root);
	erase(ch[g][0]);
	pre[ch[g][0]]=0;
    ch[g][0]=0;
    update(g);
    update(j);
    return ;
}
void change(int k, int num, int c)
{
    int j, g;
    j=select(k, root);
    g=select(k+num+1, root);
    Splay(j, 0);
    Splay(g, root);
	updatesame(ch[g][0], c);   //这里是updatesame,对ch[g][0]处理
	update(g); //更新
	update(j);
	return ;
}
void reversal(int k, int num)
{
    int j, g;
    j=select(k, root);
    g=select(k+num+1, root);
    Splay(j, 0);
    Splay(g, root);
	updaterev(ch[g][0]);
    return ;
}
int getsum(int k, int num)
{
    int j, g;
    j=select(k, root);
    g=select(k+num+1, root);
    Splay(j, 0);
    Splay(g, root);
    return sum1[ch[g][0]];
}
int getmax(int r)
{
    int s=r;
    pushdown(r);
    while(r)
    {
        s=r;
        r=ch[r][1];
        pushdown(r);
    }
    return s;
}
int maxsum()
{
    int j, g;
    j=getmin(root);
    g=getmax(root);
    Splay(j, 0);
    Splay(g, root);
	return mmax[ch[g][0]];
}
int main()
{
    int t, j, k, g;
    char p[20];
    while(scanf("%d%d", &n, &m)!=EOF)
    {
        for(t=1; t<=n; ++t)
            scanf("%d", a+t);
        init();
        while(m--)
        {
            scanf("%s", p);
            if(strcmp(p, "INSERT")==0)
            {
                scanf("%d%d", &j, &k);
                for(t=1; t<=k; ++t)
                    scanf("%d", a+t);
                insert(j, k);
                continue;
            }
            if(strcmp(p, "DELETE")==0)
            {
                scanf("%d%d", &j, &k);
                dele(j, k);
                continue;
            }
            if(strcmp(p, "MAKE-SAME")==0)
            {
                scanf("%d%d%d", &j, &k, &g);
                change(j, k, g);
                continue;
            }
            if(strcmp(p, "REVERSE")==0)
            {
                scanf("%d%d", &j, &k);
                reversal(j, k);
                continue;
            }
            if(strcmp(p, "GET-SUM")==0)
            {
                scanf("%d%d", &j, &k);
                printf("%d\n", getsum(j, k));
                continue;
            }
            if(strcmp(p, "MAX-SUM")==0)
            {
                printf("%d\n", maxsum());
            }
        }
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值