【题目】
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 > 1 Maxa-Mina>1 Maxa−Mina>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,然后分以下情况考虑:
- 若 M a x b − M i n b = 0 Maxb-Minb=0 Maxb−Minb=0,则相当于把整个区间赋成 M a x b Maxb Maxb
- 若 M a x b − M i n b ≠ 0 Maxb-Minb\ne0 Maxb−Minb̸=0,则相当于区间减去 M a x a − M a x b Maxa-Maxb Maxa−Maxb
这样的话,我们就把这道题变成一道区间覆盖,区间加,区间求和的简单题,直接套模板计算即可
【代码】
#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;
}