题目描述
有一个长度为(n)的数组(A)和(n)个区间([l_i,r_i]),有(q)次操作:
(1~x~y):把(a_x)改成(y)
(2~x~y):求第(l)个区间到第(r)个区间的区间和的和。
(n,qleq {10}^5,a_ileq {10}^9)
题解
分块。
设(s_i)为第(i)块的所有区间的区间和,(d_{i,j})为第(i)块有多少个区间包含了(j)这个位置。
修改时修改树状数组和每个区间的区间和。设当前(a_x=v),则(s_i+=(y-v) imes d_{i,x})
查询时完整的区间直接查询区间和,不完整的区间就暴力查询。
设块大小为(m),时间复杂度为
[T(n)=O(frac{n}{m}+mlog n)
]
当(m=sqrt{frac{n}{log n}})时
[T(n)_{min}=O(nsqrt{nlog n})
]
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
ull c[100010];
int a[100010];
int n;
void add(int x,ull v)
{
for(;x<=n;x+=x&-x)
c[x]+=v;
}
ull sum(int x)
{
ull s=0;
for(;x;x-=x&-x)
s+=c[x];
return s;
}
int bl;
ull s[1010];
int d[1010][100010];
int l[100010];
int r[100010];
int block[100010];
int left[100010];
int right[100010];
int main()
{
memset(c,0,sizeof c);
// freopen("xsy2111.in","r",stdin);
// freopen("xsy2111.out","w",stdout);
int m;
scanf("%d",&n);
int i;
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
add(i,a[i]);
}
bl=100;
m=(n+bl-1)/bl;
for(i=1;i<=n;i++)
block[i]=(i+bl-1)/bl;
for(i=1;i<=m;i++)
{
left[i]=(i-1)*bl+1;
right[i]=min(i*bl,n);
}
for(i=1;i<=n;i++)
{
scanf("%d%d",&l[i],&r[i]);
s[block[i]]+=sum(r[i])-sum(l[i]-1);
d[block[i]][l[i]]++;
if(r[i]<n)
d[block[i]][r[i]+1]--;
}
int j;
for(i=1;i<=m;i++)
for(j=2;j<=n;j++)
d[i][j]+=d[i][j-1];
int q;
scanf("%d",&q);
int op,x,y,k;
for(i=1;i<=q;i++)
{
scanf("%d%d%d",&op,&x,&y);
if(op==1)
{
int v=a[x];
for(j=1;j<=m;j++)
s[j]+=ull(y-v)*d[j][x];
add(x,y-v);
a[x]=y;
}
else
{
ull ans=0;
for(j=block[x];j<=block[y];j++)
if(left[j]>=x&&right[j]<=y)
ans+=s[j];
else
{
int mi=max(left[j],x);
int mx=min(right[j],y);
for(k=mi;k<=mx;k++)
ans+=sum(r[k])-sum(l[k]-1);
}
printf("%llu
",ans);
}
}
return 0;
}