GSS问题(二)

GSS问题(二)

仍然是线段树的应用模板题,非常经典

题面

(n)个数,(nleqslant1e5),和(leqslant10^{18}),全是自然数

翻译:long long能过

给出两种操作:

  • 区间开方( ightarrow)将区间每一个数单独开方,下取整
  • 区间求和( ightarrow)将区间所有书加起来求和

作为模板题,我自己给出一个条件:最好用线段树做[doge]

原题还有这样一句话:

image-20200517180233452

mmp这个造数据的比我还懒

对于细节处理问题,比如上面这张图所出现的问题就不再讲,主要是看如何用线段树实现主要操作

思路

显然,开方这个操作并不能直接对于整个区间进行

那咋办嘛?

那只能单个元素开方了

每次操作都对每一个元素进行开方,非常费时间,

但是我们知道开方可以使一个数非常快的变小,直至1,除非本来就是0或者1

那么到了0或1的时候,就无须再开方,

不妨这样考虑:

对于每一个树节点,开设变量(maxn)来保存区间最大值,如果区间最大值为1或0,显然整个区间就无须开方,直接跳过,大大优化了复杂度

#include<iostream>
#include<cstdio>
#include<cmath>
#define ll long long
#define ci const int &
using namespace std;
const int N=100005;
ll a[N];
ll sum[N<<2],maxn[N<<2];
inline void build(ci k,ci l,ci r){
	if(l==r){
		sum[k]=a[l];
		maxn[k]=a[l];
		return ;
	}int mid=l+r>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	sum[k]=sum[k<<1]+sum[k<<1|1];
	maxn[k]=max(maxn[k<<1],maxn[k<<1|1]);
}
inline void insert(ci k,ci l,ci r,ci x,ci y){
	if(maxn[k]<=1) return ;
	if(l==r){
		sum[k]=(ll)(sqrt(sum[k]));
		maxn[k]=(ll)(sqrt(maxn[k]));
		return ;
	}int mid=l+r>>1;
	if(x<=mid) insert(k<<1,l,mid,x,y);
	if(mid<y) insert(k<<1|1,mid+1,r,x,y);
	sum[k]=sum[k<<1]+sum[k<<1|1];
	maxn[k]=max(maxn[k<<1],maxn[k<<1|1]);
}
inline ll query(ci k,ci l,ci r,ci x,ci y){
	if(x<=l&&r<=y) return sum[k];
	int mid=l+r>>1;
	ll ret=0;
	if(x<=mid)  ret+=query(k<<1,l,mid,x,y);
	if(mid<y) ret+=query(k<<1|1,mid+1,r,x,y);
	return ret;
}int n,m;
int cnt;
int main(){
	while(scanf("%d",&n)!=EOF){
		printf("Case #%d:
",++cnt);
		for(int i=1;i<=n;i++)
			scanf("%lld",&a[i]);
		build(1,1,n);
		scanf("%d",&m);
		while(m--){
			int n1,n2,n3;
			scanf("%d%d%d",&n1,&n2,&n3);
			if(n2>n3) swap(n2,n3);
			if(!n1)
					insert(1,1,n,n2,n3);
			if(n1)
				printf("%lld
",query(1,1,n,n2,n3));
		}puts("");
	}
	return 0;
}
原文地址:https://www.cnblogs.com/648-233/p/12912619.html