BZOJ3211 花神游历各国

题目蓝链

Solution

由于每一个数最多被开根\(5\)次就会为\(1\),所以我们可以用一个并查集维护下一个大于\(1\)的数的位置。然后再用树状数组维护一下区间和,每次修改直接暴力改就行了,修改的时候在树状数组上更新一下

时间复杂度\(\mathcal{O}(n\ log\ log\ val + m\ logn)\)

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;

int to[maxn];
int find(int x) { return x == to[x] ? x : to[x] = find(to[x]); }

int n, m, a[maxn];

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

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

	n = read();
	for (int i = 1; i <= n + 1; i++) to[i] = i;
	for (int i = 1; i <= n; i++) BIT :: change(i, a[i] = read());

	m = read();
	while (m--) {
		int op = read();
		if (op == 1) {
			int l = read(), r = read();
			printf("%lld\n", BIT :: query(l, r));
		}else {
			int l = read(), r = read();
			for (int i = find(l); i <= r; i = (i == find(i) ? i + 1 : to[i])) {
				int tmp = sqrt(a[i]);
				BIT :: change(i, tmp - a[i]);
				a[i] = tmp;
				to[i] = a[i] > 1 ? i : i + 1;
			}
		}
	}

	return 0;
}

Summary

区间开根板子题,练练板子

我在做这道题的时候,发现了一个很神奇的东西,那就是三目运算符的常数竟然是\(if\)的五分之一!

posted @ 2018-09-26 22:34  xunzhen  阅读(80)  评论(0编辑  收藏  举报