歌名 - 歌手
0:00

    伸展树splay之求区间极值

    前言

    这篇博客是根据我在打这道题的时候遇到的问题,来打的,有些细节可能考虑不到。

    题目

    在N(1<=N<=100000)个数A1…An组成的序列上进行M(1<=M<=100000)次操作,操作有两种:
    (1)1 L R C:表示把A[L]到A[R]增加C(C的绝对值不超过10000);
    (2)2 L R:询问A[L]到A[R]之间的最大值。

    分析

    由于本人刚刚学会splay,不够精通,splay的打法这里就先不说。
    就讲讲求区间极值的方法吧。
    对于每个位置开一个节点,记录这个节点的值、在以它为根的子树中的最大值。
    当我们要对一个区间进行查询或修改时,
    假设修改的区级为\([l,r]\)
    我们将l-1转到根节点,r+1转到根节点的右儿子(为了保证有l-1和r+1节点,另外加入0和n+1节点)
    那么根据二叉查找树的性质,
    r+1的左子树就是要查询或修改的区间。
    这里写图片描述
    接着,还要处理lazy标记,
    当我们再将x节点转到y节点的儿子时只需从y到x,将标记全部下传就可以了。

    #include <cmath>
    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    const int maxlongint=2147483647;
    const int mo=1000000007;
    const int N=100005;
    using namespace std;
    int l[N],r[N],lazy[N],mx[N],a[N],fa[N],root,q1,q2;
    int tot,n,m,ans;
    void clear()
    {
    	fa[0]=lazy[0]=l[0]=r[0]=0;
    	mx[1]=mx[tot]=a[1]=a[tot]=mx[0]=a[0]=-maxlongint;
    }
    void doda(int x,int z)
    {
    	mx[x]+=z;
    	a[x]+=z;
    	lazy[x]+=z;
    }
    void down(int x)
    {
    	int z=lazy[x];
    	if(!z) return;
    	doda(l[x],z);
    	doda(r[x],z);
    	lazy[x]=0;
    }
    int getmax(int x)
    {
    	mx[x]=max(mx[l[x]],mx[r[x]]);
    	mx[x]=max(mx[x],a[x]);
    }
    void zig(int x)
    {
    	int y=fa[x];
    	fa[r[x]]=y;
    	if(l[fa[fa[x]]]==fa[x]) l[fa[y]]=x;
    	else r[fa[y]]=x;
    	l[y]=r[x];
    	r[x]=y;
    	fa[x]=fa[y];
    	fa[y]=x;
    	getmax(y);
    	getmax(x);
    }
    void zag(int x)
    {
    	int y=fa[x];
    	fa[l[x]]=y;
    	if(l[fa[fa[x]]]==fa[x]) l[fa[y]]=x;
    	else r[fa[y]]=x;
    	r[y]=l[x];
    	l[x]=y;
    	fa[x]=fa[y];
    	fa[y]=x;
    	getmax(y);
    	getmax(x);
    }
    void splay(int p,int x)
    {
    	if(x==p) return;
    	while(fa[x]!=p)
    	{
    		if(fa[fa[x]]==p)
    		{
    			if(l[fa[x]]==x) zig(x); 
    			else zag(x);
    			break;
    		}
    		q1=(l[fa[fa[x]]]==fa[x]),q2=(l[fa[x]]==x);
    		if(q1)
    		{
    			if(q2) zig(fa[x]),zig(x);
    			else zag(x),zig(x);
    		}
    		else
    		{
    			if(q2) zig(x),zag(x);
    			else zag(fa[x]),zag(x);
    		}
    	}
    }
    void sola(int v,int x)
    {
    	down(v);
    	if(x<v) sola(l[v],x);
    	if(v<x) sola(r[v],x);
    	getmax(v);
    }
    int get(int ll,int rr)
    {
    	sola(root,ll-1);
    	clear();
    	splay(0,ll-1);
    	root=ll-1;
    	sola(root,rr+1);
    	clear();
    	splay(ll-1,rr+1);
    	return l[rr+1];
    }
    int main()
    {
    	scanf("%d",&n);
    	r[1]=2;
    	fa[2]=1;
    	tot=2;
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d",&a[tot]);
    		mx[tot]=a[tot];
    		fa[tot+1]=tot;
    		r[tot]=++tot;
    	}
    	clear();
    	for(int i=tot;i>=1;i--) mx[i]=max(a[i],mx[r[i]]);
    	root=1;
    	scanf("%d",&m);
    	for(int i=1;i<=m;i++)
    	{
    		int x,ll,rr,c;
    		scanf("%d%d%d",&x,&ll,&rr);
    		ll++;
    		rr++;
    		clear();
    		if(x==1) scanf("%d",&c),doda(get(ll,rr),c);
    		else printf("%d\n",mx[get(ll,rr)]);
    	}
    }
    
    

    后记(2018.4.28)
    明天就是GDOI2018了,回顾自己以前写的博客,看到我打splay居然zig、zag分开的之类的,回忆我多年来的OI历程,有些感慨。
    总之,GDOI2018,加油。

    posted @ 2018-05-17 16:14  无尽的蓝黄  阅读(295)  评论(0编辑  收藏  举报