NAOJ38 游戏

题目蓝链

Solution

这道题我们可以先开一颗线段树来维护一下每一个角色距离升级还需的经验,这棵线段树只需要支持查询区间的(min)就可以了。如果当前区间的(min)小于(0),就说明当前的区间存在角色需要升级,我们就直接暴力递归子树,把所有该升级的角色升级。对于单点修改操作,我们就直接单独的修改一下这个点的等级就可以了

怎么保证复杂度呢?这题需要势能分析一下,一开始总的势能最大是(n cdot m),打怪会降低势能,每一次修改操作最多把势能抬高(m)。降低(1)势能需要(log)的复杂度(一般不会是满(log)),所以复杂度是可以保证的

Code

#include <bits/stdc++.h>
 
using namespace std;

#define squ(x) ((LL)(x) * (x))
#define debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long LL;
typedef pair<int, int> pii;

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

const int maxn = 1e5 + 10;
const LL inf = LLONG_MAX;

int n, m, q;
LL t[12];

int grade(LL x) { return upper_bound(t + 1, t + m + 1, x) - t - 1; }

namespace BIT {
	int A[maxn], a[maxn];
#define lowbit(x) ((x) & (-(x)))
	void change(int x, int v) { for (int i = x; i <= n; i += lowbit(i)) A[i] += v; }
	void modify(int x, int v) { change(x, v - a[x]), a[x] = v; }
	int sum(int x) { int res = 0; for (int i = x; i; i -= lowbit(i)) res += A[i]; return res; }
	int query(int l, int r) { return sum(r) - sum(l - 1); }
}

namespace ST {

	LL A[maxn << 2], tag[maxn << 2];
#define ls (rt << 1)
#define rs (rt << 1 | 1)

	void push_up(int rt) { A[rt] = min(A[ls], A[rs]); }

	void push_down(int rt) {
		if (!tag[rt]) return;
		tag[ls] += tag[rt], tag[rs] += tag[rt];
		A[ls] += tag[rt], A[rs] += tag[rt];
		tag[rt] = 0;
	}

	void build(int rt, int l, int r) {
		if (l == r) {
			A[rt] = read(); int pos = grade(A[rt]);
			A[rt] = t[pos + 1] - A[rt];
			BIT::modify(l, pos);
			return;
		}
		int mid = (l + r) >> 1;
		build(ls, l, mid);
		build(rs, mid + 1, r);
		push_up(rt);
	}

	void find(int rt, int l, int r) {
		if (l == r) {
			LL now = t[BIT::a[l] + 1] - A[rt], pos = grade(now);
			A[rt] = t[pos + 1] - now;
			BIT::modify(l, pos);
			return;
		}
		push_down(rt);
		int mid = (l + r) >> 1;
		if (A[ls] <= 0) find(ls, l, mid);
		if (A[rs] <= 0) find(rs, mid + 1, r);
		push_up(rt);
	}

	void change(int rt, int l, int r, int L, int R, int v) {
		if (L <= l && r <= R) {
			A[rt] += v, tag[rt] += v;
			if (A[rt] <= 0) find(rt, l, r);
			return;
		}
		push_down(rt);
		int mid = (l + r) >> 1;
		if (L <= mid) change(ls, l, mid, L, R, v);
		if (R > mid) change(rs, mid + 1, r, L, R, v);
		push_up(rt);
	}

	void set(int rt, int l, int r, int x, int v) {
		if (l == r) {
			int pos = grade(v);
			A[rt] = t[pos + 1] - v;
			BIT::modify(l, pos);
			return;
		}
		push_down(rt);
		int mid = (l + r) >> 1;
		if (x <= mid) set(ls, l, mid, x, v);
		else set(rs, mid + 1, r, x, v);
		push_up(rt);
	}
}

int main() {
#ifdef xunzhen
	freopen("38.in", "r", stdin);
	freopen("38.out", "w", stdout);
#endif

	n = read(), m = read(), q = read();
	for (int i = 1; i <= m; i++) t[i] = read(); t[m + 1] = inf;
	ST::build(1, 1, n);

	while (q--) {
		int op = read();
		if (op == 1) {
			int l = read(), r = read(), x = read();
			ST::change(1, 1, n, l, r, -x);
		}
		if (op == 2) {
			int p = read(), x = read();
			ST::set(1, 1, n, p, x);
		}
		if (op == 3) {
			int l = read(), r = read();
			printf("%d
", BIT::query(l, r));
		}
	}

	return 0;
}

Summary

这道题我一开始看题的时候没有看到(m = 10)这个条件,结果想了好久...

以后做这种需要用线段树维护的题目的时候,一定要先想一下有没有什么操作的总次数是在时间可接受的范围之内的,有就可以直接暴力维护一下就可以了

原文地址:https://www.cnblogs.com/xunzhen/p/9678013.html