吉老师线段树(hdu5306 bzoj4695)

前言:

今天突然想学一学这个,那就学一学,资料可以在lzz的uoj博客找到。

核心:

(我只学了区间取 min ⁡ \min min max ⁡ \max max,不会历史最值)
以取 min ⁡ \min min为例子,那么我们需要维护区间最大值 m x mx mx,次大值 s e se se,最大值出现次数 c c c,区间和 s u m sum sum,然后分下面几种情况(设当前区间的值改为 min ⁡ ( a i , v ) \min(a_i,v) min(ai,v)):
1、 v > = m x v>=mx v>=mx,直接返回,无需修改。
2、 s e &lt; v &lt; m x se&lt;v&lt;mx se<v<mx s u m = s u m − ( m x − v ) × c sum=sum-(mx-v)\times c sum=sum(mxv)×c,返回。
3、直接递归到 2 2 2个儿子。
复杂度不知道,吉老师说可以证到 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n),详情见这里

hdu5306:

这个题比较简单,因为只需要一个最大值标记,直接按照上面说的搞就好了,但是多组数据,注意初始化,我的初始化就有问题,不知为什么,要全清为 0 0 0才可以。

代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int Maxn=1000010;
const int inf=2147483647;
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<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
int n,m,a[Maxn];
struct Seg{int l,r,lc,rc,mx,se,c,tag;LL sum;}tr[Maxn<<1];
int len;
void up(int x)
{
    int lc=tr[x].lc,rc=tr[x].rc;
    tr[x].sum=tr[lc].sum+tr[rc].sum;
    if(tr[lc].mx==tr[rc].mx)
    {
        tr[x].mx=tr[lc].mx;
        tr[x].se=max(tr[lc].se,tr[rc].se);
        tr[x].c=tr[lc].c+tr[rc].c;
    }
    else
    {
        tr[x].mx=max(tr[lc].mx,tr[rc].mx);
        tr[x].se=max(tr[lc].se,tr[rc].se);
        tr[x].se=max(tr[x].se,((tr[lc].mx>tr[rc].mx)?tr[rc].mx:tr[lc].mx));
        tr[x].c=((tr[lc].mx>tr[rc].mx)?tr[lc].c:tr[rc].c);
    }
}
bool work(int x,int v)
{
    if(tr[x].mx<=v)return true;
    if(tr[x].se<v)
    {
        tr[x].sum-=(LL)tr[x].c*(tr[x].mx-v);
        tr[x].mx=tr[x].tag=v;
        return true;
    }
    return false;
}
void down(int x)
{
    int lc=tr[x].lc,rc=tr[x].rc;
    work(lc,tr[x].tag),work(rc,tr[x].tag);
    tr[x].tag=-1;
}
void build(int l,int r)
{
    int t=++len;
    tr[t].l=l;tr[t].r=r;tr[t].tag=-1;
    if(l==r){tr[t].sum=tr[t].mx=a[l];tr[t].c=1;tr[t].se=-1;return;}
    int mid=l+r>>1;
    tr[t].lc=len+1,build(l,mid);
    tr[t].rc=len+1,build(mid+1,r);
    up(t);
}
void modify(int x,int l,int r,int v)
{   
    int mid=tr[x].l+tr[x].r>>1,lc=tr[x].lc,rc=tr[x].rc;
    if(tr[x].tag!=-1)down(x);
    if(l<=tr[x].l&&tr[x].r<=r)
    {
        if(work(x,v))return;
        modify(lc,l,mid,v),modify(rc,mid+1,r,v);
        up(x);
        return;
    }
    if(r<=mid)modify(lc,l,r,v);
    else if(l>mid)modify(rc,l,r,v);
    else modify(lc,l,mid,v),modify(rc,mid+1,r,v);
    up(x);
}
LL query(int x,int l,int r,int o)
{
    int mid=tr[x].l+tr[x].r>>1,lc=tr[x].lc,rc=tr[x].rc;
    if(tr[x].tag!=-1)down(x);
    if(tr[x].l==l&&tr[x].r==r)
    {
        if(!o)return tr[x].mx;
        return tr[x].sum;
    }
    if(r<=mid)return query(lc,l,r,o);
    if(l>mid)return query(rc,l,r,o);
    if(o)return query(lc,l,mid,o)+query(rc,mid+1,r,o);
    return max(query(lc,l,mid,o),query(rc,mid+1,r,o));
}
int main()
{
    int T=read();
    while(T--)
    {
        n=read(),m=read();
        for(int i=1;i<=n;i++)a[i]=read();
        for(int i=1;i<=(n<<1);i++)tr[i].lc=tr[i].rc=tr[i].mx=tr[i].se=tr[i].tag=tr[i].sum=tr[i].c=0;
        len=0;
        build(1,n);
        while(m--)
        {
            int op=read(),l=read(),r=read();
            if(op)printf("%lld\n",query(1,l,r,op-1));
            else modify(1,l,r,read());
        }
    }
}

bzoj4695:

这个复杂一点,要同时维护最大最小、次大次小这些标记,区间取 max ⁡ \max max min ⁡ \min min的时候可能对这些值都有影响,都要维护到,而且感觉有点儿卡常数,加了些优化才过的。

代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define lc (x<<1)
#define rc (x<<1|1)
const int Maxn=500010;
const int inf=2147483647;
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<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*f;
}
void write(LL x)
{
    if(x==0){putchar('0');putchar('\n');return;}
    if(x<0)putchar('-'),x=-x;
    int a[20],l=0;
    while(x)a[++l]=x%10+48,x/=10;
    for(int i=l;i;i--)putchar(a[i]);
    putchar('\n');
}
int n,m,a[Maxn];
struct Seg{int l,r,mx,mn,cmx,cmn;LL mx1,mn1,sum,tag;}tr[Maxn<<2];
int len=0;
void up(int x)
{
	tr[x].sum=tr[lc].sum+tr[rc].sum;
	if(tr[lc].mx==tr[rc].mx)
	{
		tr[x].mx=tr[lc].mx;
		tr[x].mx1=max(tr[lc].mx1,tr[rc].mx1);
		tr[x].cmx=tr[lc].cmx+tr[rc].cmx;
	}
	else
	{
		tr[x].mx=max(tr[lc].mx,tr[rc].mx);
		tr[x].mx1=max(tr[lc].mx1,tr[rc].mx1);
		tr[x].mx1=max(tr[x].mx1,(LL)((tr[lc].mx>tr[rc].mx)?tr[rc].mx:tr[lc].mx));
		tr[x].cmx=((tr[lc].mx>tr[rc].mx)?tr[lc].cmx:tr[rc].cmx);
	}
	if(tr[lc].mn==tr[rc].mn)
	{
		tr[x].mn=tr[lc].mn;
		tr[x].mn1=min(tr[lc].mn1,tr[rc].mn1);
		tr[x].cmn=tr[lc].cmn+tr[rc].cmn;
	}
	else
	{
		tr[x].mn=min(tr[lc].mn,tr[rc].mn);
		tr[x].mn1=min(tr[lc].mn1,tr[rc].mn1);
		tr[x].mn1=min(tr[x].mn1,(LL)((tr[lc].mn>tr[rc].mn)?tr[lc].mn:tr[rc].mn));
		tr[x].cmn=((tr[lc].mn>tr[rc].mn)?tr[rc].cmn:tr[lc].cmn);
	}
}
bool w1(int x,int v)
{
	if(tr[x].mx<=v)return true;
	if(tr[x].mx==tr[x].mn)
	{
		tr[x].mx=tr[x].mn=v;
		tr[x].cmx=tr[x].cmn=tr[x].r-tr[x].l+1;
		tr[x].mx1=-(1LL<<62),tr[x].mn1=(1LL<<62);
		tr[x].sum=(LL)tr[x].cmx*v;
		return true;
	}
	if(tr[x].mx1<v)
	{
		tr[x].sum-=(LL)tr[x].cmx*(tr[x].mx-v);
		tr[x].mx=v;tr[x].mn1=min(tr[x].mn1,(LL)v);tr[x].mn=min(tr[x].mn,v);
		return true;
	}
	return false;
}
bool w2(int x,int v)
{
	if(tr[x].mn>=v)return true;
	if(tr[x].mx==tr[x].mn)
	{
		tr[x].mx=tr[x].mn=v;
		tr[x].cmx=tr[x].cmn=tr[x].r-tr[x].l+1;
		tr[x].mx1=-(1LL<<62),tr[x].mn1=(1LL<<62);
		tr[x].sum=(LL)tr[x].cmx*v;
		return true;
	}
	if(tr[x].mn1>v)
	{
		tr[x].sum+=(LL)tr[x].cmn*(v-tr[x].mn);
		tr[x].mn=v;tr[x].mx1=max(tr[x].mx1,(LL)v);tr[x].mx=max(tr[x].mx,v);
		return true;
	}
	return false;
}
void Add(int x,LL v)
{
	tr[x].mx+=v,tr[x].mn+=v,tr[x].mx1+=v,tr[x].mn1+=v;
	tr[x].sum+=v*(tr[x].r-tr[x].l+1);
	tr[x].tag+=v;
}
void down(int x)
{
	if(tr[x].tag)Add(lc,tr[x].tag),Add(rc,tr[x].tag),tr[x].tag=0;
	if(tr[lc].mx>tr[x].mx)w1(lc,tr[x].mx);
	if(tr[rc].mx>tr[x].mx)w1(rc,tr[x].mx);
	if(tr[lc].mn<tr[x].mn)w2(lc,tr[x].mn);
	if(tr[rc].mn<tr[x].mn)w2(rc,tr[x].mn);
}
void build(int x,int l,int r)
{
	tr[x].l=l;tr[x].r=r;tr[x].tag=0;
	if(l==r)
	{tr[x].sum=tr[x].mx=tr[x].mn=a[l];tr[x].cmx=tr[x].cmn=1;tr[x].mx1=-(1LL<<62);tr[x].mn1=(1LL<<62);return;}
	int mid=l+r>>1;
	build(lc,l,mid);
	build(rc,mid+1,r);
	up(x);
}
void add(int x,int l,int r,int v)
{
	if(tr[x].l!=tr[x].r)down(x);
	if(tr[x].l==l&&tr[x].r==r){Add(x,v);return;}
	int mid=tr[x].l+tr[x].r>>1;
	if(r<=mid)add(lc,l,r,v);
	else if(l>mid)add(rc,l,r,v);
	else add(lc,l,mid,v),add(rc,mid+1,r,v);
	up(x);
}
void m1(int x,int l,int r,int v)
{
	if(tr[x].mx<=v)return;
	int mid=tr[x].l+tr[x].r>>1;
	if(tr[x].l!=tr[x].r)down(x);
	if(l<=tr[x].l&&tr[x].r<=r)
	{
		if(w1(x,v))return;
		m1(lc,l,mid,v),m1(rc,mid+1,r,v);
		up(x);
		return;
	}
	if(r<=mid)m1(lc,l,r,v);
	else if(l>mid)m1(rc,l,r,v);
	else m1(lc,l,mid,v),m1(rc,mid+1,r,v);
	up(x);
}
void m2(int x,int l,int r,int v)
{
	if(tr[x].mn>=v)return;
	int mid=tr[x].l+tr[x].r>>1;
	if(tr[x].l!=tr[x].r)down(x);
	if(l<=tr[x].l&&tr[x].r<=r)
	{
		if(w2(x,v))return;
		m2(lc,l,mid,v),m2(rc,mid+1,r,v);
		up(x);
		return;
	}
	if(r<=mid)m2(lc,l,r,v);
	else if(l>mid)m2(rc,l,r,v);
	else m2(lc,l,mid,v),m2(rc,mid+1,r,v);
	up(x);
}
LL query(int x,int l,int r,int o)
{
	int mid=tr[x].l+tr[x].r>>1;
	if(tr[x].l!=tr[x].r)down(x);
	if(tr[x].l==l&&tr[x].r==r)
	{
		if(!o)return tr[x].sum;
		if(o==1)return tr[x].mx;
		return tr[x].mn;
	}
	if(r<=mid)return query(lc,l,r,o);
	if(l>mid)return query(rc,l,r,o);
	if(!o)return query(lc,l,mid,o)+query(rc,mid+1,r,o);
	if(o==1)return max(query(lc,l,mid,o),query(rc,mid+1,r,o));
	return min(query(lc,l,mid,o),query(rc,mid+1,r,o));
}
int main()
{
	n=read();
	for(int i=1;i<=n;i++)a[i]=read();
	build(1,1,n);
	m=read();
	while(m--)
	{
		int op=read(),l=read(),r=read();
		if(op>3)write(query(1,l,r,op-4));
		else
		{
			int x=read();
			if(op==1)add(1,l,r,x);
			else if(op==2)m2(1,l,r,x);
			else m1(1,l,r,x);
		}
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值