洛谷4145 上帝造题的七分钟2 / 花神游历各国(线段树)

传送门

【题目分析】

题目大概就是让你维护一个数据结构,支持区间开方和区间求和————线段树!

区间求和是基本操作,所以考虑如何支持区间开方。

考虑一个区间a[l]~a[r],令其中最大值为a[i],最小值为a[j],所以区间极差就为a[i]-a[j],如果我们对其进行开方操作,那么极差就变为了\sqrt{a[i]}-\sqrt{a[j]},很明显能发现,极差变小了,所以我们得出结论:对于每一次开方操作,区间内的数都逐渐趋向相近,那么对于一个区间进行几次操作后,极差就小于等于1了,所以我们维护区间最大值,看操作前后是否超过1,如果超过就递归修改,否则就相当于进行一次区间覆盖操作。

【代码~】

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL MAXN=1e5+10;

struct Tree{
	LL l,r;
	LL maxx,sum;
}tr[MAXN<<2];
LL n,q;
LL a[MAXN];

void push_up(LL root)
{
	tr[root].maxx=max(tr[root<<1].maxx,tr[root<<1|1].maxx);
	tr[root].sum=tr[root<<1].sum+tr[root<<1|1].sum;
}

void build(LL root,LL l,LL r)
{
	tr[root].l=l;
	tr[root].r=r;
	if(l==r)
	{
		tr[root].maxx=tr[root].sum=a[l];
		return ;
	}
	LL mid=l+r>>1;
	build(root<<1,l,mid);
	build(root<<1|1,mid+1,r);
	push_up(root);
}

void update(LL root,LL L,LL R)
{
	if(tr[root].l>R||tr[root].r<L||tr[root].maxx<=1)
	  return ;
	if(tr[root].l==tr[root].r)
	{
		tr[root].maxx=tr[root].sum=sqrt(tr[root].maxx);
		return ;
	}
	LL mid=tr[root].l+tr[root].r>>1;
	if(L<=mid)
	  update(root<<1,L,R);
	if(R>mid)
	  update(root<<1|1,L,R);
	push_up(root);
}

LL query(LL root,LL l,LL r,LL L,LL R)
{
	if(L<=l&&r<=R)
	  return tr[root].sum;
	if(r<L||l>R)
	  return 0;
	LL mid=l+r>>1,ret=0;
	if(L<=mid)
	  ret+=query(root<<1,l,mid,L,R);
	if(R>mid)
	  ret+=query(root<<1|1,mid+1,r,L,R);
	return ret;
}

LL Read()
{
	LL i=0,f=1;
	char c;
	for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
	if(c=='-')
	  f=-1,c=getchar();
	for(;c>='0'&&c<='9';c=getchar())
	  i=(i<<1)+(i<<3)+c-'0';
	return i*f;
}

void sc(LL x)
{
	if(x>=10)
	  sc(x/10);
	putchar(x%10+48);
}

int main()
{
	n=Read();
	for(LL i=1;i<=n;++i)
	  a[i]=Read();
	build(1,1,n);
	q=Read();
	while(q--)
	{
		LL cz=Read(),l=Read(),r=Read();
		if(l>r)
		{
			LL t=l;
			l=r;
			r=t;
		}
		if(!cz)
		  update(1,l,r);
		else
		  sc(query(1,1,n,l,r)),puts("");
	}
	return 0;
}

 

posted @ 2018-10-22 12:18  Ishtar~  阅读(144)  评论(0编辑  收藏  举报