【HDU 5828】Rikka with Sequence

【题目】

传送门

Problem Description

As we know, Rikka is poor at math. Yuta is worrying about this situation, so he gives Rikka some math tasks to practice. There is one of them:

Yuta has an array A with n numbers. Then he makes m operations on it.

There are three type of operations:

1 l r x : For each i in [l,r], change A[i] to A[i]+x
2 l r : For each i in [l,r], change A[i] to ⌊ A [ i ] \sqrt {A[i]} A[i]
3 l r : Yuta wants Rikka to sum up A[i] for all i in [l,r]

It is too difficult for Rikka. Can you help her?

Input

The first line contains a number t(1<=t<=100), the number of the testcases. And there are no more than 5 testcases with n>1000.

For each testcase, the first line contains two numbers n,m(1<=n,m<=100000). The second line contains n numbers A[1]~A[n]. Then m lines follow, each line describe an operation.

It is guaranteed that 1<=A[i],x<=100000.

Output

For each operation of type 3, print a lines contains one number – the answer of the query.

Sample Input

1
5 5
1 2 3 4 5
1 3 5 2
2 1 4
3 2 4
2 3 5
3 1 5

Sample Output

5
6


【分析】

题目大意:维护一种数据结构,使它能支持区间开方区间加区间求和

对于一个区间,显然在开几次方之后区间的极差会很小,并且对整个区间进行区间加操作是对区间的极差没有影响的,这个极差随着修改数的增多仍然是单调不增的,也就是说,在对这个区间进行多次(可以看成常数次)开方操作之后,整个区间的极差就不会超过 1 1 1

对每个区间记录一个最大值 M a x a Maxa Maxa,最小值 M i n a Mina Mina,若 M a x a − M i n a &gt; 1 Maxa-Mina&gt;1 MaxaMina>1 就递归修改

不然的话,我们取 M a x b = M a x a , M i n b = M i n a Maxb=\sqrt {Maxa},Minb=\sqrt {Mina} Maxb=Maxa ,Minb=Mina ,然后分以下情况考虑:

  1. M a x b − M i n b = 0 Maxb-Minb=0 MaxbMinb=0,则相当于把整个区间赋成 M a x b Maxb Maxb
  2. M a x b − M i n b ≠ 0 Maxb-Minb\ne0 MaxbMinb̸=0,则相当于区间减去 M a x a − M a x b Maxa-Maxb MaxaMaxb

这样的话,我们就把这道题变成一道区间覆盖区间加区间求和的简单题,直接套模板计算即可


【代码】

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1000005
#define ll long long
using namespace std;
int n,m,a[N];
ll sum[N<<2],Max[N<<2],Min[N<<2],add[N<<2],mark[N<<2];
void Pushup(int root)
{
	sum[root]=sum[root<<1]+sum[root<<1|1];
	Max[root]=max(Max[root<<1],Max[root<<1|1]);
	Min[root]=min(Min[root<<1],Min[root<<1|1]);
}
void coverit(int root,int l,int r,ll val)
{
	sum[root]=val*(r-l+1);
	Max[root]=Min[root]=val;
	add[root]=0,mark[root]=val;
}
void addit(int root,int l,int r,ll val)
{
	Max[root]+=val;
	Min[root]+=val;
	add[root]+=val;
	sum[root]+=val*(r-l+1);
}
void Pushdown(int root,int l,int r,int mid)
{
	if(mark[root]!=-1)  coverit(root<<1,l,mid,mark[root]),coverit(root<<1|1,mid+1,r,mark[root]);
	if(add[root])  addit(root<<1,l,mid,add[root]),addit(root<<1|1,mid+1,r,add[root]);
	add[root]=0,mark[root]=-1;
}
void Build(int root,int l,int r)
{
	add[root]=0;
	mark[root]=-1;
	if(l==r)
	{
		sum[root]=Max[root]=Min[root]=a[l];
		return;
	}
	int mid=(l+r)>>1;
	Build(root<<1,l,mid);
	Build(root<<1|1,mid+1,r);
	Pushup(root);
}
void Add(int root,int l,int r,int x,int y,ll k)
{
	if(l>=x&&r<=y)
	{
		addit(root,l,r,k);
		return;
	}
	int mid=(l+r)>>1;
	Pushdown(root,l,r,mid);
	if(x<=mid)  Add(root<<1,l,mid,x,y,k);
	if(y>mid)  Add(root<<1|1,mid+1,r,x,y,k);
	Pushup(root);
}
void Modify(int root,int l,int r,int x,int y)
{
	if(l>=x&&r<=y&&Max[root]-Min[root]<=1)
	{
		ll maxn=sqrt(Max[root]),minn=sqrt(Min[root]);
		if(maxn-minn==0)  coverit(root,l,r,maxn);
		else  addit(root,l,r,maxn-Max[root]);
		return;
	}
	int mid=(l+r)>>1;
	Pushdown(root,l,r,mid);
	if(x<=mid)  Modify(root<<1,l,mid,x,y);
	if(y>mid)  Modify(root<<1|1,mid+1,r,x,y);
	Pushup(root);
}
ll Query(int root,int l,int r,int x,int y)
{
	if(l>=x&&r<=y)
	  return sum[root];
	ll ans=0;int mid=(l+r)>>1;
	Pushdown(root,l,r,mid);
	if(x<=mid)  ans+=Query(root<<1,l,mid,x,y);
	if(y>mid)  ans+=Query(root<<1|1,mid+1,r,x,y);
	return ans;
}
int main()
{
	int t,i;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&m);
		for(i=1;i<=n;++i)
		  scanf("%d",&a[i]);
		Build(1,1,n);
		int s,l,r,x;
		for(i=1;i<=m;++i)
		{
			scanf("%d%d%d",&s,&l,&r);
			if(s==1)  scanf("%d",&x),Add(1,1,n,l,r,x);
			else  if(s==2)  Modify(1,1,n,l,r);
			else  printf("%lld\n",Query(1,1,n,l,r));
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值