博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

bzoj 3211 花神游历各国 线段树区间开方

题目大意

给定一个序列,支持区间开方、区间求和

\(n \leq 10^5,a_i \leq 10^9\)

思路

由于\(a_i \leq 10^9\),因此每一个元素至多开方五次就会变为1,因此每个点至多会被修改5次,复杂度\(O(nlogn)\)

代码

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

struct node {
	int l,r;
	ll sum;
	ll set;
}t[N << 2];

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

void update(int rt) {
	int ch = rt << 1;
	t[rt].sum = t[ch].sum + t[ch + 1].sum;
	t[rt].set = (t[ch].set && t[ch + 1].set);
}

void build(int l,int r,int rt) {
	t[rt].l = l;
	t[rt].r = r;
	if (l == r) {
		t[rt].sum = a[l];
		t[rt].set = (a[l] <= 1);
		return;
	}
	int mid = (l + r) >> 1;
	int ch = rt << 1;
	build(l,mid,ch);
	build(mid + 1,r,ch + 1);
	update(rt);
}

void modify(int l,int r,int rt) {
	if(t[rt].set) { //优化,如果当前不需要开方就不开了
		return;
	}
	if(t[rt].l == t[rt].r) {
		t[rt].sum = (ll)sqrt(t[rt].sum + 0.5);
		t[rt].set = t[rt].sum <= 1;
	}
	else {
		int mid = (t[rt].l + t[rt].r) >> 1;
		int ch = rt << 1;
		if(r <= mid) {
			modify(l,r,ch);
		}else if(l > mid) {
			modify(l,r,ch + 1);
		}else {
			modify(l,mid,ch);
			modify(mid + 1,r,ch + 1);
		}
		update(rt);
	}
}


ll query(int l,int r,int rt) {
	//cout << l << ' ' << r << endl;
	if(l <= t[rt].l and t[rt].r <= r) return t[rt].sum;
	int mid = (t[rt].l + t[rt].r) >> 1;
	ll res = 0;
	int ch = rt << 1;
	if(l <= mid) {
		res += query(l,r,ch);
	}
	if(r > mid) {
		res += query(l,r,ch + 1);
	}
	return res;
}

int n,m;

int main () {
	n = read();
	for(int i = 1;i <= n; i ++) a[i] = read();
	build(1,n,1);
	m = read();
	while(m --) {
		int x,l,r;
		x = read(),l = read(),r = read();
		if(x == 1) {
			printf("%lld\n",query(l,r,1));
		}
		else {
			modify(l,r,1);
		}
	}
	return 0;
}
posted @ 2022-02-13 20:41  Allorkiya  阅读(27)  评论(0编辑  收藏  举报