[题解]LuoGu4146:序列终结者

255 篇文章 0 订阅
11 篇文章 0 订阅

原题传送门
序列终结者,用它来终结我这一阶段 S p l a y Splay Splay的练习再好不过了。。

三个操作,区间加,区间翻转,区间最值

最近以Splay为核心,看到翻转就想到用Splay,加和翻转用懒标记可以解决,同时可以维护一个最值,由pushup来更新size和mx(区间最值数组)

Code:

#include <iostream> 
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cctype>
#define maxn 100010
using namespace std;
int rt, sz, n, m, f[maxn], size[maxn], val[maxn], add[maxn], tag[maxn], mx[maxn], son[maxn][2];

inline int read(){
    int s = 0, w = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
    for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
    return s * w;
}

void pushup(int x){
    size[x] = size[son[x][0]] + size[son[x][1]] + 1;
    mx[x] = max(val[x], max(mx[son[x][0]], mx[son[x][1]]));
}

int get(int x){ return son[f[x]][1] == x; }

void connect(int x, int y, int z){
    f[x] = y, son[y][z] = x;
}

void rotate(int x){
    int fa = f[x], ffa = f[fa], m = get(x), n = get(fa);
    connect(son[x][m ^ 1], fa, m);
    connect(fa, x, m ^ 1);
    connect(x, ffa, n);
    pushup(fa); pushup(x);
}

void splay(int x, int goal){
    while (f[x] != goal){
        int fa = f[x];
        if (f[fa] != goal) rotate(get(x) == get(fa) ? fa : x);
        rotate(x);
    }
    if (!goal) rt = x;
}

int build(int l, int r, int fa){
    if (l > r) return 0;
    int x = ++sz, mid = (l + r) >> 1;
    f[x] = fa, size[x] = 1, val[x] = mx[x] = 0;
    if (mid == 1 || mid == n + 2) val[x] = mx[x] = -2e9;
    son[x][0] = build(l, mid - 1, x), son[x][1] = build(mid + 1, r, x);
    pushup(x); return x;
}

void pushdown(int x){
    if (add[x]){
        if (son[x][0]) add[son[x][0]] += add[x], val[son[x][0]] += add[x], mx[son[x][0]] += add[x];
        if (son[x][1]) add[son[x][1]] += add[x], val[son[x][1]] += add[x], mx[son[x][1]] += add[x];
    }
    if (tag[x]){
        tag[son[x][0]] ^= 1; tag[son[x][1]] ^= 1; swap(son[x][0], son[x][1]);
    }
    add[x] = tag[x] = 0;
    return;
}

int kth(int x){
    int now = rt;
    while (1){
        pushdown(now);
        if (size[son[now][0]] >= x) now = son[now][0]; else
        if (size[son[now][0]] + 1 >= x) return now; else
        x -= size[son[now][0]] + 1, now = son[now][1];
    }
}

int main(){
    n = read(), m = read();
    mx[0] = -2e9;
    rt = build(1, n + 2, 0);
    while (m--){
        int opt = read(), l = read(), r = read();
        l = kth(l), r = kth(r + 2);
        splay(l, 0); splay(r, l);
        int u = son[son[rt][1]][0];
        if (opt == 1){
            int k = read();
            add[u] += k, val[u] += k, mx[u] += k; 
        } else
        if (opt == 2) tag[u] ^= 1; else printf("%d\n", mx[u]);
    }
    return 0;
}

当然也可以用fhq Treap
跟上面一样,不过方便非常多
简直无脑。。。
Code:

#include <bits/stdc++.h>
#define maxn 100010
using namespace std;
int rt, sz, val[maxn], size[maxn], key[maxn], son[maxn][2], add[maxn], mx[maxn], tag[maxn], n, m;

inline int read(){
	int s = 0, w = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
	for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
	return s * w;
}

int addnode(){
	++sz;
	val[sz] = 0, key[sz] = rand() * rand(), size[sz] = 1;
	return sz;
}

void pushup(int x){
	size[x] = size[son[x][0]] + size[son[x][1]] + 1;
	mx[x] = max(val[x], max(mx[son[x][0]], mx[son[x][1]]));
}

void pushdown(int x){
	if (add[x]){
		if (son[x][0]) add[son[x][0]] += add[x], val[son[x][0]] += add[x], mx[son[x][0]] += add[x];
		if (son[x][1]) add[son[x][1]] += add[x], val[son[x][1]] += add[x], mx[son[x][1]] += add[x];
		add[x] = 0;
	}
	if (tag[x]){
		tag[son[x][0]] ^= 1, tag[son[x][1]] ^= 1;
		swap(son[x][0], son[x][1]);
		tag[x] = 0;
	} 
}

void split(int now, int w, int &u, int &v){
	if (!now) u = v = 0; else{
		pushdown(now);
		if (size[son[now][0]] >= w) v = now, split(son[now][0], w, u, son[v][0]); else
		u = now, split(son[now][1], w - size[son[now][0]] - 1, son[u][1], v);
		pushup(now);
	} 
}

int merge(int u, int v){
	if (!u || !v) return u + v;
	if (key[u] >= key[v]){
		pushdown(u);
		son[u][1] = merge(son[u][1], v);
		pushup(u);
		return u;
	} else{
		pushdown(v);
		son[v][0] = merge(u, son[v][0]);
		pushup(v);
		return v;
	}
}

int main(){
	srand(time(0));
	n = read(), m = read();
	mx[0] = -2e9;
	for (int i = 1; i <= n; ++i) rt = merge(rt, addnode());
	while (m--){
		int opt = read(), l = read(), r = read(), x, y, z;
		split(rt, r, x, y); split(x, l - 1, x, z);
		if (opt == 1){
			int k = read();
			add[z] += k, val[z] += k, mx[z] += k;
		} else
		if (opt == 2) tag[z] ^= 1; else printf("%d\n", mx[z]);
		rt = merge(merge(x, z), y);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值