1. 程式人生 > 其它 >bzoj 3211 花神遊歷各國 線段樹區間開方

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;
}