• 区间历史最值笔记——线段树


    例题

    CPU监控
    要你维护对序列上的操作:
    1、区间加
    2、区间赋值
    3、区间最大值
    4、区间历史最值

    使用线段树+标记维护,记录节点上发生的所有事件。

    注意到一个线段树节点,如果进行了modify操作,那么接下来的加法都可以认为是modify。

    那么一个节点上的标记长度就至多为2了。

    ( ext{add}) 标记时节点实际要加的值,( ext{mod}) 表示覆盖。
    考虑记录 ( ext{Add}) 标记为所有祖先的 ( ext{add}) 标记,历史上能达到的最大值。( ext{Mod}) 同理。
    下放标记时注意到子节点上的标记发生时间在该点之前,依此时间顺序进行合并如下:

    当下放加法标记,一方面由于 ( ext{Add}) 是上方加法操作中最大增加的值,用它结合实际值来更新历史最值
    若子节点无 ( ext{mod}) 标记那么标记向 ( ext{add}) 上打,否则向 ( ext{mod}) 上打。
    并且要结合该子节点当前的操作值,更新对应操作的历史最值

    当下放覆盖标记,先用 ( ext{Mod}) (历史最大覆盖值)更新子节点的历史最值和 ( ext{Mod})
    然后用实际覆盖值修改实际最大值和实际覆盖值。

    下放标记要注意顺序,加法在先,赋值在后。由于该写法下放标记的操作数量很小,可能存在速度优势。

    下面是这个题的代码,它在2020年1月19日是洛谷的Rank1.

    #include<stdio.h>
    #include<algorithm>
    using namespace std;
    
    const int N = 100005;
    const int inf = 1e9;
    
    struct IO_tp
    {
    	static const int Sbuf=1<<21;
    	char buf[Sbuf], *S, *T, c; int f;
    	#define gc() (S==T?(T=(S=buf)+fread(buf,1,sizeof(buf),stdin),(S==T?EOF:*S++)):*S++)
    	template<class I>
    	inline IO_tp& operator >> (I &x)
    	{
    		for(f=1, c=gc(); c<'0'||c>'9'; c=gc()) if(c=='-') f=-1;
    		for(x=0; c>='0'&&c<='9'; c=gc()) x=x*10+(c^48); x*=f;
    		return *this;
    	}
    	inline char Readc()
    	{
    		for(c=gc(); c<'A'||c>'Z'; c=gc());
    		return c;
    	}
    }io;
    
    inline void ckmax(int&x,const int y)
    { x = x < y ? y : x; }
    
    struct Segtree
    {
    	struct node {
    		int max, add, mod;
    		int Max, Add, Mod;
    		node(): mod(-inf), Mod(-inf) {}
    	}t[N << 2];
    	
    	#define lc (o << 1)
    	#define rc (o << 1 | 1)
    	
    	void pushup(int o)
    	{
    		t[o].max = max(t[lc].max, t[rc].max);
    		t[o].Max = max(t[lc].Max, t[rc].Max);
    	}
    	
    	void dadd(int o, int x, int y)
    	{
    		ckmax(t[o].Max, t[o].max + x);
    		t[o].max += y;
    		if(t[o].mod == -inf)
    			ckmax(t[o].Add, t[o].add + x), t[o].add += y;
    		else
    			ckmax(t[o].Mod, t[o].mod + x), t[o].mod += y;
    	}
    	
    	void dmod(int o, int x, int y)
    	{
    		ckmax(t[o].Max, x);
    		ckmax(t[o].Mod, x);
    		t[o].mod = t[o].max = y;
    	}
    	
    	void pushdown(int o)
    	{
    		if(t[o].add || t[o].Add)
    		{
    			dadd(lc, t[o].Add, t[o].add);
    			dadd(rc, t[o].Add, t[o].add);
    			t[o].Add = t[o].add = 0;
    		}
    		if(t[o].mod != -inf)
    		{
    			dmod(lc, t[o].Mod, t[o].mod);
    			dmod(rc, t[o].Mod, t[o].mod);
    			t[o].Mod = t[o].mod = -inf;
    		}
    	}
    	
    	void build(int o, int l, int r)
    	{
    		if(l == r)
    		{
    			int k; io >> k;
    			t[o].max = t[o].Max = k;
    			return;
    		}
    		int mid = (l + r) >> 1;
    		build(lc, l, mid); build(rc, mid + 1, r);
    		pushup(o);
    	}
    	
    	void update(int o, int l, int r, int pl, int pr, int x)
    	{
    		if(l > pr || r < pl)
    			return;
    		if(l >= pl && r <= pr)
    		{
    			dadd(o, x, x);
    			return;
    		}
    		pushdown(o);
    		int mid = (l + r) >> 1;
    		update(lc, l, mid, pl, pr, x);
    		update(rc, mid + 1, r, pl, pr, x);
    		pushup(o);
    	}
    	
    	void modify(int o, int l, int r, int pl, int pr, int x)
    	{
    		if(l > pr || r < pl)
    			return;
    		if(l >= pl && r <= pr)
    		{
    			dmod(o, x, x);
    			return;
    		}
    		pushdown(o);
    		int mid = (l + r) >> 1;
    		modify(lc, l, mid, pl, pr, x);
    		modify(rc, mid + 1, r, pl, pr, x);
    		pushup(o);
    	}
    	
    	int query(int o, int l, int r, int pl, int pr)
    	{
    		if(l > pr || r < pl)
    			return -inf;
    		if(l >= pl && r <= pr)
    			return t[o].max;
    		pushdown(o);
    		int mid = (l + r) >> 1;
    		return max(query(lc, l, mid, pl, pr), query(rc, mid + 1, r, pl, pr));
    	}
    	
    	int Query(int o, int l, int r, int pl, int pr)
    	{
    		if(l > pr || r < pl)
    			return -inf;
    		if(l >= pl && r <= pr)
    			return t[o].Max;
    		pushdown(o);
    		int mid = (l + r) >> 1;
    		return max(Query(lc, l, mid, pl, pr), Query(rc, mid + 1, r, pl, pr));
    	}
    }z[1];
    
    int main()
    {
    	freopen("cpu.in", "r", stdin);
    	freopen("cpu.out", "w", stdout);
    	int n, q, l, r, x; char opt[3];
    	io >> n;
    	z->build(1, 1, n);
    	io >> q;
    	while(q--)
    	{
    		opt[0] = io.Readc();
    		io >> l >> r;
    		if(*opt == 'Q')
    			printf("%d
    ", z->query(1, 1, n, l, r));
    		if(*opt == 'A')
    			printf("%d
    ", z->Query(1, 1, n, l, r));
    		if(*opt == 'P')
    			io >> x, z->update(1, 1, n, l, r, x);
    		if(*opt == 'C')
    			io >> x, z->modify(1, 1, n, l, r, x);
    	}
    	return 0;
    }
    

    上面的做法有局限性。
    设分段函数 (f(x)=max(x+a,b)),则区间修改操作均可以写成对一个区间作用上一个函数。
    并且 (h(x)=max(f(x),g(x))) 也依然是这样的函数。
    那么我们可以维护区间的分段函数 (f(x)) 和当前所有函数“最高轮廓” (g(x))
    考虑下放标记,(f(x)) 直接合并即可,考虑 (g(x))
    相当于一堆函数作用到一个函数 (f') 上,最高的轮廓应当是这堆函数的 (g) 作用于 (f),然后再把这个和之前的 (g') 取较高的。
    若要维护区间历史最值,下放标记时用 (g( ext{max})) 更新 ( ext{Max});再令 ( ext{max}=f( ext{max}))
    要注意你这个操作是对于这个区间整体而言的,就是说该区间的历史最值肯定要找最靠右的位置,所以要用当时的 ( ext{max}) 更新。

    下面是清华集训那题的代码,只有单点查询(区间历史最值只要在 pushdown 里加两句话)

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    
    typedef long long ll;
    const int N = 500004;
    const ll inf = 1e16;
    
    inline void ckmax(ll&x, ll y)
    { x = x < y ? y : x; }
    
    struct IO_tp
    {
    	static const int Sbuf=1<<21;
    	char buf[Sbuf], *S, *T, c; int f;
    	#define gc() (S==T?(T=(S=buf)+fread(buf,1,sizeof(buf),stdin),(S==T?EOF:*S++)):*S++)
    	template<class I>
    	inline IO_tp& operator >> (I &x)
    	{
    		for(f=1, c=gc(); c<'0'||c>'9'; c=gc()) if(c=='-') f=-1;
    		for(x=0; c>='0'&&c<='9'; c=gc()) x=x*10+(c^48); x*=f;
    		return *this;
    	}
    	inline char Readc()
    	{
    		for(c=gc(); c<'A'||c>'Z'; c=gc());
    		return c;
    	}
    }io;
    
    int n, m, a[N];
    
    struct node {
    	ll a, b;
    	node(ll p = 0, ll q = -inf): a(p), b(q) {}
    	
    	node operator + (const node& t)
    	{ return node(max(a + t.a, -inf), max(b + t.a, t.b)); }
    	
    	ll operator () (ll x)
    	{ return max(x + a, b); }
    	
    	void operator |= (const node& t)
    	{
    		ckmax(a, t.a);
    		ckmax(b, t.b);
    	}
    } null;
    
    struct Segtree
    {
    	#define lc (o << 1)
    	#define rc (o << 1 | 1)
    	
    	node f[N << 2], g[N << 2];
    	
    	void Z(int o, const node& p, const node& q)
    	{
    		f[o] |= g[o] + p;
    		g[o] = g[o] + q;
    	}
    	
    	void pushdown(int o)
    	{
    		Z(lc, f[o], g[o]); Z(rc, f[o], g[o]);
    		f[o] = g[o] = null;
    	}
    	
    	void modify(int o, int l, int r, int pl, int pr, const node& z)
    	{
    		if(l > pr || r < pl)
    			return;
    		if(l >= pl && r <= pr)
    		{
    			Z(o, z, z);
    			return;
    		}
    		pushdown(o);
    		int mid = (l + r) >> 1;
    		modify(lc, l, mid, pl, pr, z);
    		modify(rc, mid + 1, r, pl, pr, z);
    	}
    	
    	ll query(int o, int l, int r, int p)
    	{
    		if(l == r)
    			return g[o](a[l]);
    		pushdown(o);
    		int mid = (l + r) >> 1;
    		return mid >= p ? query(lc, l, mid, p) : query(rc, mid + 1, r, p);
    	}
    	
    	ll Query(int o, int l, int r, int p)
    	{
    		if(l == r)
    			return f[o](a[l]);
    		pushdown(o);
    		int mid = (l + r) >> 1;
    		return mid >= p ? Query(lc, l, mid, p) : Query(rc, mid + 1, r, p);
    	}
    }z[1];
    
    int main()
    {
    	io >> n >> m;
    	for(int i = 1; i <= n; ++i)
    		io >> a[i];
    	while(m--)
    	{
    		int opt, l, r, x;
    		io >> opt;
    		if(opt <= 3)
    		{
    			io >> l >> r >> x;
    			if(opt == 1)
    				z->modify(1, 1, n, l, r, node(x, 0));
    			else if(opt == 2)
    				z->modify(1, 1, n, l, r, node(-x, 0));
    			else
    				z->modify(1, 1, n, l, r, node(-inf, x));
    		}
    		else
    		{
    			io >> x;
    			if(opt == 4)
    				printf("%lld
    ", z->query(1, 1, n, x));
    			else
    				printf("%lld
    ", z->Query(1, 1, n, x));
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    凹凸函数
    HashMap数据类型使用注意不能使用基本数据类型
    Tomcat部署发布JSP应用程序的三种方法
    Ubuntu忘记密码,进不了系统的解决方法
    matlab之运算符及其优先级
    java和tomcat配置
    MySQL中 MySQL X.X Command Line Client 一闪而过
    Pearson(皮尔逊)相关系数
    CG, DCG, NDCG
    C#中ListBox中SelectedItem使用注意
  • 原文地址:https://www.cnblogs.com/bestwyj/p/12215649.html
Copyright © 2020-2023  润新知