http://uoj.ac/problem/228
sylvia 是一个热爱学习的女孩子,今天她想要学习数据结构技巧。
在看了一些博客学了一些姿势后,她想要找一些数据结构题来练练手。于是她的好朋友九条可怜酱给她出了一道题。
给出一个长度为 nn 的数列 AA,接下来有 mm 次操作,操作有三种:
对于所有的 i∈[l,r] 将 Ai 变成 Ai+x。
对于所有的 i∈[l,r] 将 Ai 变成 ⌊√Ai⌋
对于所有的 i∈[l,r] 询问 Ai 的和。
作为一个不怎么熟练的初学者,sylvia 想了好久都没做出来。而可怜酱又外出旅游去了,一时间联系不上。于是她决定向你寻求帮助:你能帮她解决这个问题吗。
输入格式
第一行两个数:n,mn,m。
接下来一行 nn 个数 AiAi。
接下来 mm 行中,第 ii 行第一个数 titi 表示操作类型:
若 ti=1ti=1,则接下来三个整数 li,ri,xili,ri,xi,表示操作一。
若 ti=2ti=2,则接下来三个整数 li,rili,ri,表示操作二。
若 ti=3ti=3,则接下来三个整数 li,rili,ri,表示操作三。
输出格式
对于每个询问操作,输出一行表示答案。
样例一
input
5 5
1 2 3 4 5
1 3 5 2
2 1 4
3 2 4
2 3 5
3 1 5
output
5
6
思路:这题暴力开根会TLE。考虑维护一个区间最大值MAX和区间最小值MIN,当 M A X = M I N MAX=MIN MAX=MIN或者 M A X − M A X = M I N − M I N MAX-\sqrt{MAX}=MIN-\sqrt{MIN} MAX−MAX=MIN−MIN 时,区间开根操作就可以转换成区间加法操作,提高效率。
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
struct node
{
int l,r;
ll sum,MAX,MIN,lazy;
};
node tree[maxn<<2];
inline int read()
{
int x=0,f=1;
char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')
ch=getchar();
if(ch=='-')
f=-1,ch=getchar();
while(ch>='0'&&ch<='9')
x=x*10+ch-48,ch=getchar();
return x*f;
}
inline void down(int i)
{
int l=i<<1,r=i<<1|1;
tree[l].lazy+=tree[i].lazy;
tree[r].lazy+=tree[i].lazy;
tree[l].sum+=(tree[l].r-tree[l].l+1)*tree[i].lazy;
tree[r].sum+=(tree[r].r-tree[r].l+1)*tree[i].lazy;
tree[l].MAX+=tree[i].lazy;
tree[l].MIN+=tree[i].lazy;
tree[r].MAX+=tree[i].lazy;
tree[r].MIN+=tree[i].lazy;
tree[i].lazy=0;
}
inline void up(int i)
{
int l=i<<1,r=i<<1|1;
tree[i].sum=tree[l].sum+tree[r].sum;
tree[i].MAX=max(tree[l].MAX,tree[r].MAX);
tree[i].MIN=min(tree[l].MIN,tree[r].MIN);
}
void build(int i,int l,int r)
{
tree[i].l=l,tree[i].r=r;
tree[i].lazy=0;
if(l==r)
{
tree[i].MAX=tree[i].MIN=tree[i].sum=read();
return ;
}
int mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
up(i);
}
ll querysum(int i,int l,int r)
{
if(tree[i].l==l&&tree[i].r==r)
return tree[i].sum;
if(tree[i].lazy)
down(i);
int mid=(tree[i].l+tree[i].r)>>1;
if(r<=mid)
return querysum(i<<1,l,r);
else if(l>mid)
return querysum(i<<1|1,l,r);
else
return querysum(i<<1,l,mid)+querysum(i<<1|1,mid+1,r);
}
void update1(int i,int l,int r,ll v)
{
if(tree[i].l==l&&tree[i].r==r)
{
tree[i].sum+=(r-l+1)*v;
tree[i].MAX+=v;
tree[i].MIN+=v;
tree[i].lazy+=v;
return ;
}
if(tree[i].lazy)
down(i);
int mid=(tree[i].l+tree[i].r)>>1;
if(r<=mid)
update1(i<<1,l,r,v);
else if(l>mid)
update1(i<<1|1,l,r,v);
else
update1(i<<1,l,mid,v),
update1(i<<1|1,mid+1,r,v);
up(i);
}
void update2(int i,int l,int r)
{
if(tree[i].l==l&&tree[i].r==r)
{
ll a=sqrt(tree[i].MAX);
ll b=sqrt(tree[i].MIN);
ll v=a-tree[i].MAX;
int flag=0;
if(tree[i].MAX==tree[i].MIN||tree[i].MAX-a==tree[i].MIN-b)
flag=1;
if(flag)
{
tree[i].sum+=(r-l+1)*v;
tree[i].MAX+=v;
tree[i].MIN+=v;
tree[i].lazy+=v;
return ;
}
}
if(tree[i].lazy)
down(i);
int mid=(tree[i].l+tree[i].r)>>1;
if(r<=mid)
update2(i<<1,l,r);
else if(l>mid)
update2(i<<1|1,l,r);
else
update2(i<<1,l,mid),
update2(i<<1|1,mid+1,r);
up(i);
}
int main()
{
int n,m,op,l,r;
ll v;
n=read(),m=read();
build(1,1,n);
for(int i=0;i<m;i++)
{
op=read(),l=read(),r=read();
if(op==1)
{
v=read();
update1(1,l,r,v);
}
else if(op==2)
update2(1,l,r);
else
printf("%lld\n",querysum(1,l,r));
}
return 0;
}