UOJ228/HDU5828 基础数据结构练习题/Rikka with Sequence 题解(势能线段树)

势能线段树。如果线段树上一个节点的 maxmin2,我们称其为关键节点,考虑定义势能 ϕ 为线段树上关键节点的个数。

对于每次开方操作,如果当前节点为关键节点,则暴力递归左右儿子修改,否则:

  1. 如果当前节点 max=minmax=min+1max 不是完全平方数,则相当区间覆盖为 max 或区间减去 max
  2. 如果当前节点 max=min+1max 完全平方数,则相当于区间减 maxmax(因为此时对于最小值,Δ=minmin=maxmax

考虑一次区间加操作,最多可以产生 O(logn) 个关键区间(由线段树区间加的时间复杂度正确性保证),且对于一个关键区间,我们至多做 O(loglogV) 次开方操作,他就不再是一个关键区间(考虑极端情况 min=1,max=V)。

于是最终时间复杂度 O(nlognloglogV)

注意:不能以 minmax 判断是否为关键区间,否则会被 8,9,8,92,3,2,38,9,8,9 这种数据卡掉。

const int MAXN = 1e5 + 5, inf = MAXN * INT_MAX;
int n, m, a[MAXN];
struct _node {
	int sm, ad, st, mx, mn;
} tr[MAXN << 2];

int read() {
	int x = 0, f = 1;
	char ch = getchar();
	while (!isdigit(ch)) {
		if (ch == '-') f = -1;
		ch = getchar();
	}
	while (isdigit(ch)) {
		x = x * 10 + ch - '0';
		ch = getchar();
	}
	return x * f;
}

void pushup(int p) {
	tr[p].sm = tr[lson].sm + tr[rson].sm;
	tr[p].mx = max(tr[lson].mx, tr[rson].mx);
	tr[p].mn = min(tr[lson].mn, tr[rson].mn);
}

void build(int p, int l, int r) {
	if (l == r) {
		tr[p] = {a[l], 0, inf, a[l], a[l]};
		return;
	}
	tr[p].ad = 0, tr[p].st = inf;
	build(lson, l, mid);
	build(rson, mid + 1, r);
	pushup(p);
}

void addst(int p, int l, int r, int v) {
	tr[p].sm = (r - l + 1) * v;
	tr[p].mx = tr[p].mn = v;
	tr[p].ad = 0;
	tr[p].st = v;
}

void addtg(int p, int l, int r, int v) {
	if (tr[p].st != inf) tr[p].st += v;
	else tr[p].ad += v; 
	tr[p].sm += (r - l + 1) * v;
	tr[p].mx += v;
	tr[p].mn += v;
}

void pushdown(int p, int l, int r) {
	if (tr[p].st != inf) {
		addst(lson, l, mid, tr[p].st);
		addst(rson, mid + 1, r, tr[p].st);
		tr[p].st = inf;
	}
	if (tr[p].ad) {
		addtg(lson, l, mid, tr[p].ad);
		addtg(rson, mid + 1, r, tr[p].ad);
		tr[p].ad = 0;
	}
}

void modify(int p, int l, int r, int L, int R, int x) {
	if (L <= l && r <= R) return addst(p, l, r, x);
	pushdown(p, l, r);
	if (L <= mid) modify(lson, l, mid, L, R, x);
	if (mid < R) modify(rson, mid + 1, r, L, R, x);
	pushup(p); 
}


void modifySqrt(int p, int l, int r, int L, int R) {
	if (L <= l && r <= R && tr[p].mx - tr[p].mn <= 1) {
		const int val = sqrtl(tr[p].mx);
		if (tr[p].mx - tr[p].mn == 1 && val * val == tr[p].mx) return addtg(p, l, r, val - tr[p].mx);
		return addst(p, l, r, val);
	}
	pushdown(p, l, r);
	if (L <= mid) modifySqrt(lson, l, mid, L, R);
	if (mid < R) modifySqrt(rson, mid + 1, r, L, R);
	pushup(p);
}

void add(int p, int l, int r, int L, int R, int x) {
	if (L <= l && r <= R) return addtg(p, l, r, x);
	pushdown(p, l, r);
	if (L <= mid) add(lson, l, mid, L, R, x);
	if (mid < R) add(rson, mid + 1, r, L, R, x);
	pushup(p);
}

int querySum(int p, int l, int r, int L, int R) {
	if (L <= l && r <= R) return tr[p].sm;
	pushdown(p, l, r);
	int ret = 0;
	if (L <= mid) ret += querySum(lson, l, mid, L, R);
	if (mid < R) ret += querySum(rson, mid + 1, r, L, R);
	return ret;
}

void work() {
	n = read(); m = read();
	for (int i = 1; i <= n; ++i) a[i] = read();
	build(1, 1, n);
	while (m--) {
		int op, l, r, x;
		op = read(); l = read(); r = read();
		if (op == 1) {
			x = read();
			add(1, 1, n, l, r, x);
		}
		if (op == 2) {
			modifySqrt(1, 1, n, l, r);
		}
		if (op == 3) {
			printf("%lld\n", querySum(1, 1, n, l, r));
		}
	}
}
posted @   小蛐蛐awa  阅读(44)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示