题目链接:https://vjudge.net/problem/HDU-5828
参考博客:
http://www.cnblogs.com/forever97/p/hdu5828.html
http://blog.youkuaiyun.com/zzz805/article/details/52193127
区间开方不好维护,但是多次开方后区间极差会快速降低到小于等于1。
我们在区间极差大于1的时候暴力更新。
在小于等于1的时候讨论。
如果区间极差为0,那么整个区间的元素都相等,可以直接维护(区间赋值或区间更新)。
如果区间极差为1,那么整个区间只有两种相差1的值,再讨论下开方后两种值是否相等还是依然相差1。
如果开方后相等,那么就直接维护(区间赋值),否则就可以转化为区间加上一个值(区间更新)。
具体算法就是线段树维护区间和,区间最大值,区间最小值。极差可以直接算出来。
延迟标记维护区间增减。
开方操作转化成区间增减或区间赋值。
区间赋值就是一个flag标记,表示这个区间内所有的值都相等,要往下push,flag可以直接由区间最值判断。
pushdown函数写错了,少写了add(ls),add(rs)的更新,WA了好久。
自己对本题完全没有想法,还是经验不够丰富,思考确实很重要,但也要多学习别人的方法。
长代码中的小错误很难搞定,哪怕自己带数据也很难找到错误。
写代码的时候再小心一点吧,检查的时候要从多个层次考虑把,包括细节,实现,算法。
查错可以带数据,检查,对拍。
但能够避免错误是最好的。
本题是利用了区间开方可以缩小区间极差的特点。然后当区间极差比较小的时候,可以通过讨论将区间开方转化为区间增减或区间赋值。
代码
#include<stdio.h>
#include<algorithm>
#include<math.h>
#define ls (now<<1)
#define rs (ls|1)
#define m ((l+r)>>1)
using namespace std;
typedef long long ll;
const int maxn = 100010;
int n,q;
int A[maxn];
/////////////////////线段树
ll tree[maxn<<2][3];
ll add[maxn<<2];
void maintain(int now)
{
tree[now][0]=tree[ls][0]+tree[rs][0];
tree[now][1]=max(tree[ls][1],tree[rs][1]);
tree[now][2]=min(tree[ls][2],tree[rs][2]);
}
void pushdown(int l,int r,int now)
{
if(tree[now][1]==tree[now][2])
{
tree[ls][0]=tree[now][1]*(m-l+1);
tree[rs][0]=tree[now][1]*(r-m);
for(int i=1;i<=2;i++) tree[ls][i]=tree[rs][i]=tree[now][i];
add[now]=0;
}
else if(add[now])
{
tree[ls][0]+=add[now]*(m-l+1);
tree[rs][0]+=add[now]*(r-m);
for(int i=1;i<=2;i++)
{
tree[ls][i]+=add[now];
tree[rs][i]+=add[now];
}
add[ls]+=add[now];
add[rs]+=add[now];
add[now]=0;
}
}
void build(int l,int r,int now)
{
add[now]=0;
if(l==r)
{
for(int i=0;i<3;i++) tree[now][i]=A[l];
return;
}
build(l,m,ls);
build(m+1,r,rs);
maintain(now);
}
void update1(int l,int r,int now,int ql,int qr,ll val)
{
if(l>qr||r<ql) return;
if(ql<=l&&r<=qr)
{
tree[now][0]+=val*(r-l+1);
for(int i=1;i<=2;i++) tree[now][i]+=val;
add[now]+=val;
return;
}
pushdown(l,r,now);
update1(l,m,ls,ql,qr,val);
update1(m+1,r,rs,ql,qr,val);
maintain(now);
}
void update2(int l,int r,int now,int ql,int qr)
{
if(l>qr||r<ql) return;
if(ql<=l&&r<=qr)
{
if(tree[now][1]==tree[now][2])
{
ll val = floor(sqrt(tree[now][1]))-tree[now][1];
tree[now][0]+=val*(r-l+1);
for(int i=1;i<=2;i++) tree[now][i]+=val;
add[now]+=val;
return;
}
if(tree[now][1]==tree[now][2]+1)
{
ll s = floor(sqrt(tree[now][2]));
ll b = floor(sqrt(tree[now][1]));
if(s==b)
{
tree[now][0]=b*(r-l+1);
tree[now][1]=tree[now][2]=b;
}
else
{
ll val = b-tree[now][1];
tree[now][0]+=val*(r-l+1);
for(int i=1;i<=2;i++) tree[now][i]+=val;
add[now]+=val;
}
return;
}
}
pushdown(l,r,now);
update2(l,m,ls,ql,qr);
update2(m+1,r,rs,ql,qr);
maintain(now);
}
ll qry(int l,int r,int now,int ql,int qr)
{
if(l>qr||r<ql) return 0;
if(ql<=l&&r<=qr) return tree[now][0];
pushdown(l,r,now);
return qry(l,m,ls,ql,qr)+qry(m+1,r,rs,ql,qr);
}
/////////////////////线段树
void read()
{
scanf("%d %d",&n,&q);
for(int i=1;i<=n;i++)
scanf("%d",A+i);
}
void solve()
{
read();
build(1,n,1);
int p,l,r,x;
while(q--)
{
scanf("%d",&p);
if(p==1)
{
scanf("%d %d %d",&l,&r,&x);
update1(1,n,1,l,r,x);
}
else if(p==2)
{
scanf("%d %d",&l,&r);
update2(1,n,1,l,r);
}
else
{
scanf("%d %d",&l,&r);
printf("%lld\n",qry(1,n,1,l,r));
}
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--) solve();
return 0;
}