【GDOI2019模拟2019.4.13】数据结构

Description:

在这里插入图片描述
1<=n,m<=1e5

时限:-O2,1s
空限:128MB

题解:

把询问区间的左端点视为x,右端点视为y。

转换模型后不难发现就是对二维平面上一些东西做一个东西。

设s表示一段时间内这个区间和的增量,v表示和的增量的历史版本最小值(v>=0)。

这个玩意显然是可以合并的,即有两个相邻的时间段的s和v,就可以合并出新的。

那么用二维线段树去维护这个吗?

空间爆了,且常数巨大。

所以可以用KD-tree维护

Code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
#define pp printf
#define ll long long
using namespace std;

const int N = 1e5 + 5;

int n, m, a[N], aa[N];
ll sa[N];

struct nod {
	int op, x, y;
} b[N];

struct P {
	int d[2], i;
} c[N]; int c0;

int D;

int cmp(P a, P b) {
	return a.d[D] < b.d[D];
}

struct hh {
	int l, r;
	int d[2], mx[2], mi[2];
	ll s, v, ls, lv;
} t[N];

int rt;

int To[N];

void mer(hh &a, hh b) {
	fo(j, 0, 1) a.mi[j] = min(a.mi[j], b.mi[j]), a.mx[j] = max(a.mx[j], b.mx[j]);
}
void bt(int &i, int x, int y) {
	int m = x + y >> 1; i = m;
	nth_element(c + x, c + m, c + y + 1, cmp);
	fo(j, 0, 1) t[i].mx[j] = t[i].mi[j] = t[i].d[j] = c[m].d[j];
	To[c[m].i] = m;
	D ^= 1;
	if(x < m) bt(t[i].l, x, m - 1), mer(t[i], t[t[i].l]);
	if(m < y) bt(t[i].r, m + 1, y), mer(t[i], t[t[i].r]);
	D ^= 1;
}

int pl, pr, px, py;
ll ps, pv;

void add(ll &s, ll &v, ll ss, ll vv) {
	v = min(v, s + vv);
	s += ss;
}
void down(int i) {
	if(t[i].ls || t[i].lv) {
		add(t[t[i].l].s, t[t[i].l].v, t[i].ls, t[i].lv);
		add(t[t[i].l].ls, t[t[i].l].lv, t[i].ls, t[i].lv);
		add(t[t[i].r].s, t[t[i].r].v, t[i].ls, t[i].lv);
		add(t[t[i].r].ls, t[t[i].r].lv, t[i].ls, t[i].lv);
		t[i].ls = t[i].lv = 0;
	}
}
void add(int i, int x, int y) {
	if(t[i].mx[0] < pl || t[i].mi[0] > pr) return;
	if(t[i].mx[1] < px || t[i].mi[1] > py) return;
	if(t[i].mi[0] >= pl && t[i].mx[0] <= pr)
		if(t[i].mi[1] >= px && t[i].mx[1] <= py) {
			add(t[i].s, t[i].v, ps, pv);
			add(t[i].ls, t[i].lv, ps, pv);
			return;
		}
	if(t[i].d[0] >= pl && t[i].d[0] <= pr)
		if(t[i].d[1] >= px && t[i].d[1] <= py)
			add(t[i].s, t[i].v, ps, pv);
	int m = x + y >> 1; down(i);
	if(x < m) add(t[i].l, x, m - 1);
	if(m < y) add(t[i].r, m + 1, y);
}
void ft(int i, int x, int y) {
	if(y < pl || x > pr) return;
	int m = x + y >> 1;
	if(pl == m) {
		pv = t[i].v; return;
	}
	down(i);
	if(pl < m) ft(t[i].l, x, m - 1); else
	ft(t[i].r, m + 1, y);
}

int main() {
	freopen("ds.in", "r", stdin);
	freopen("ds.out", "w", stdout);
	scanf("%d %d", &n, &m);
	fo(i, 1, n) scanf("%d", &a[i]), sa[i] = sa[i - 1] + a[i], aa[i] = a[i];
	fo(i, 1, m) {
		scanf("%d %d %d", &b[i].op, &b[i].x, &b[i].y);
		if(b[i].op == 2) {
			c0 ++;
			c[c0].i = i;
			c[c0].d[0] = b[i].x;
			c[c0].d[1] = b[i].y;
		}
	}
	bt(rt, 1, c0);
	fo(i, 1, m) {
		if(b[i].op == 1) {
			pl = 1, pr = b[i].x;
			px = b[i].x; py = n;
			ps = b[i].y - a[b[i].x]; pv = min(0ll, ps);
			a[b[i].x] = b[i].y;
			add(rt, 1, c0);
		} else {
			pl = pr = To[i];
			pv = 0;
			ft(rt, 1, c0);
			pp("%lld\n", pv + sa[b[i].y] - sa[b[i].x - 1]);
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值