VEXOBEN
Vexoben
Mar 16, 2019
It takes 8 minutes to read this article.

题目链接:UOJ228 基础数据结构练习题

(刷新以获取数学公式)

题意

给出一个长度为 的数列 ,接下来有 次操作,操作有三种:

  1. 对于所有的 ,将 变成
  2. 对于所有的 ,将 变成
  3. 对于所有的 ,询问 的和。

数据范围:

题解

用线段树做。

不考虑区间加,那么每个数最多做就会变为,于是只要判断当前区间有没有大于的数,有就向下递归计算即可。

考虑区间加。一个直观的想法是,如果一些数在之后得到的是相同的数,这个区间内就相当于区间赋值;否则递归下去计算。不幸的是,这样的复杂度并不正确。可以和之间互相转化,每次都将复杂度卡满。

可以换一个思路。如果一个区间内,后最大数的变化量和最小数的变化量相等,就转化为区间减法,否则就向下递归。这样一个数会被递归到,要么是源自初始序列要次降为,要么是源自这个位置位于某一次加法操作的端点上。也就是:向下递归相当于回收加法产生的差分数组的两个标记。这样总复杂度就是(此处认为,同阶)

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 1e5 + 10;

int n, m;
int a[N];
LL tr[N << 2], mx[N << 2], mi[N << 2], tag[N << 2];

void read(int &x) {
	char c = getchar();
	while (c < '0' || c > '9') c = getchar();
	for (x = 0; c >= '0' && c <= '9'; c = getchar())
		x = (x << 3) + (x << 1) + c - '0';
}

void Pushup(int x) {
	tr[x] = tr[x << 1] + tr[x << 1 | 1];
	mx[x] = max(mx[x << 1], mx[x << 1 | 1]);
	mi[x] = min(mi[x << 1], mi[x << 1 | 1]);
}

void Build(int x, int l, int r) {
	if (l == r) {
		tr[x] =  mi[x] = mx[x] = a[l];
		return;
	}
	int mid = (l + r) >> 1;
	Build(x << 1, l, mid);
	Build(x << 1 | 1, mid + 1, r);
	Pushup(x);
}

void Pushdown(int x, int l, int r) {
	if (tag[x]) {
		int mid = (l + r) >> 1;
		tr[x << 1] += 1LL * (mid - l + 1) * tag[x];
		mi[x << 1] += tag[x];
		mx[x << 1] += tag[x];
		tag[x << 1] += tag[x];
		tr[x << 1 | 1] += 1LL * (r - mid) * tag[x];
		mi[x << 1 | 1] += tag[x];
		mx[x << 1 | 1] += tag[x];
		tag[x << 1 | 1] += tag[x];
		tag[x] = 0;
	}
}

void Update1(int x, int l, int r, int ql, int qr, int del) {
	if (ql <= l && r <= qr) {
		tr[x] += 1LL * (r - l + 1) * del;
		mx[x] += del;
		mi[x] += del;
		tag[x] += del;
		return;
	}
	int mid = (l + r) >> 1;
	Pushdown(x, l, r);
	if (mid >= ql) Update1(x << 1, l, mid, ql, qr, del);
	if (mid < qr) Update1(x << 1 | 1, mid + 1, r, ql, qr, del);
	Pushup(x);
}

void Update2(int x, int l, int r, int ql, int qr) {
	if (ql <= l && r <= qr) {
		LL u = sqrt(mx[x]), v = sqrt(mi[x]);
		if (mx[x] - u == mi[x] - v) {
			LL del = mx[x] - u;
			tag[x] -= del;
			mx[x] -= del;
			mi[x] -= del;
			tr[x] -= 1LL * del * (r - l + 1);
			return;
		}
	}
	Pushdown(x, l, r);
	int mid = (l + r) >> 1;
	if (mid >= ql) Update2(x << 1, l, mid, ql, qr);
	if (mid < qr) Update2(x << 1 | 1, mid + 1, r, ql, qr);
	Pushup(x);
}

LL Query(int x, int l, int r, int ql, int qr) {
	if (ql <= l && r <= qr) return tr[x];
	Pushdown(x, l, r);
	int mid = (l + r) >> 1;
	LL ans = 0;
	if (mid >= ql) ans += Query(x << 1, l, mid, ql, qr);
	if (mid < qr) ans += Query(x << 1 | 1, mid + 1, r, ql, qr);
	return ans;
}

int main() {
	read(n); read(m);
	for (int i = 1; i <= n; ++i) {
		read(a[i]);
	}
	Build(1, 1, n);
	while (m--) {
		int opt, l, r, x;
		read(opt); read(l); read(r);
		if (opt == 1) {
			read(x);
			Update1(1, 1, n, l, r, x);
		}
		if (opt == 2) {
			Update2(1, 1, n, l, r);
		}
		if (opt == 3) {
			printf("%lld\n", Query(1, 1, n, l, r));
		}
	}
	return 0;
}