BZOJ 3211 花神游历各国 线段树

Description

Input

Output

每次x=1时,每行一个整数,表示这次旅行的开心度

Sample Input

4

1 100 5 5

5

1 1 2

2 1 2

1 1 2

2 2 3

1 1 4

Sample Output

101

11

11

HINT

对于100%的数据, n ≤ 100000,m≤200000 ,data[i]非负且小于10^9


Source



花神系列中的水题,,当然得做做。
题目就是让你维护一个区间开平方以及区间求和的序列……

其实可以看到,即便最大最大的非高精度数字(2^63-1),开平方的话没几次就变1了。
所以其实可以对于开方操作,首先暴力开平方,
然后维护一下一段数字内是不是全部<=1和区间的和。
因为如果数字<=1就不会变了,所以不需要给它们继续开方。
这题就成了暴力+维护区间和。

以前hdu4027写过,题意基本一致。
以前写了分块,就想着贴代码……因为数据看上去差不多。
结果坑了,竟然被卡掉了1s多。。
没有办法了,,只好写一份线段树了。

比想象中写得快……而且速度也比预期快了不少。
对比一下线段树和分块的时间吧……
感觉其实两者谁更优从这道题中可以简单看到了。
但是事实上根据不同的题目,大常数的线段树还是有可能不行的呢。


#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll read(){
    ll x=(ll)0,f=(ll)1;char ch=getchar();
    while (ch<'0' || ch>'9'){if (ch=='-') f=(ll)-1;ch=getchar();}
    while (ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int
    N=100005;
int n,q;
ll tree[N<<2],a[N];
bool mark[N<<2];
void build(int id,int l,int r){
	if (l==r){
		tree[id]=a[r];
		if (tree[id]<=1) mark[id]=1;
		return;
	}
	int mid=(l+r)/2;
	build(id<<1,l,mid);
	build((id<<1)+1,mid+1,r);
	tree[id]=tree[id<<1]+tree[id<<1|1];
	mark[id]=mark[id<<1]&mark[id<<1|1];
}
void update(int id,int l,int r,int gx,int gy){
	if (l==r){
		tree[id]=(ll)sqrt(tree[id]);
		if (tree[id]<=1) mark[id]=1;
		return;
	}
	int mid=(l+r)>>1;
	if (gx<=mid && !mark[id<<1]) update(id<<1,l,mid,gx,gy);
	if (gy>mid && !mark[(id<<1)+1])	 update((id<<1)+1,mid+1,r,gx,gy);
	tree[id]=tree[id<<1]+tree[id<<1|1];
	mark[id]=mark[id<<1]&mark[id<<1|1];
}
ll query(int id,int l,int r,int gx,int gy){
	if (gx<=l && gy>=r) return tree[id];
	int mid=(l+r)>>1; ll pp=(ll)0;
	if (gx<=mid) pp+=query(id<<1,l,mid,gx,gy);
	if (gy>mid)	pp+=query((id<<1)+1,mid+1,r,gx,gy);
	return pp;
}
int main() {
	n=read();
	memset(mark,0,sizeof(mark));
	for (int i=1;i<=n;i++) a[i]=read();
	build(1,1,n);
	int opt,x,y; q=read();
	for (int i=1;i<=q;i++){
		opt=read(),x=read(),y=read();
		if (opt==1)
			printf("%lld\n",query(1,1,n,x,y));
				else update(1,1,n,x,y);
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值