题意:一个数列a,若干个函数,每个函数j=sigma(a[i]),l[j]<=i<=r[j]。
op1:修改数列a中x位置元素为y。op2:求L~R的函数和。n<=1e5。
标程:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef unsigned long long ll; 4 int read() 5 { 6 int x=0,f=1;char ch=getchar(); 7 while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();} 8 while (ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); 9 return x*f; 10 } 11 const int N=100005; 12 int n,blo,a[N],l[N],r[N],b[320][N],q,x,y,op; 13 ll sum[N],k1[N],k2[N],s[N]; 14 15 int trans1(int x){return x>blo*blo?blo:(x-1)/blo+1;}//所在块 16 int trans2(int x){int t=trans1(x);return t==blo?n:t*blo;}//所在块的右端点 17 18 void modi(int pos,int x) 19 { 20 for (int i=trans1(pos);i<=blo;i++) k1[i]+=x; 21 for (int i=pos,t=trans2(pos);i<=t;i++) k2[i]+=x; 22 } 23 24 ll qry(int x){ 25 return s[x]+k1[trans1(x)-1]+k2[x]; 26 } 27 28 ll calc(int x) 29 { 30 ll res=0;int id=trans1(x); 31 for (int i=1;i<id;i++) res+=sum[i]; 32 for (int i=(id-1)*blo+1;i<=x;i++) res+=qry(r[i])-qry(l[i]-1); 33 return res; 34 } 35 36 int main() 37 { 38 n=read();blo=(int)sqrt(n); 39 for (int i=1;i<=n;i++) a[i]=read(),s[i]=(ll)s[i-1]+a[i]; 40 for (int i=1;i<=n;i++) l[i]=read(),r[i]=read(); 41 42 for (int i=1;i<=blo;i++) 43 { 44 int bl=(i-1)*blo+1,br=(i==blo)?n:i*blo; 45 for (int j=bl;j<=br;j++) b[i][l[j]]++,b[i][r[j]+1]--; 46 for (int j=1;j<=n;j++) b[i][j]+=b[i][j-1]; 47 for (int j=1;j<=n;j++) sum[i]+=(ll)b[i][j]*a[j]; 48 } 49 50 q=read(); 51 while (q--) 52 { 53 op=read();x=read();y=read(); 54 if (op==1) 55 { 56 for (int i=1;i<=blo;i++) sum[i]+=(ll)b[i][x]*(y-a[x]); 57 modi(x,y-a[x]);a[x]=y; 58 }else printf("%llu\n",calc(y)-calc(x-1)); 59 } 60 return 0; 61 }
易错点:1.没有开ll挂了90分。不要以为ll是小问题。
2.还要unsigned long long。
3.注意左右端点,尤其是最后一块。写个trans函数比较直观。
题解:分块+块链
对函数进行分块,每一块维护每个数列元素的需统计次数以及sum。(每个元素的出现次数可以用差分+前缀和)。
查询的时候整块暴力统计sum,零碎的直接统计即可。
零碎的统计要支持修改和区间查询,树状数组(插入查询O(logn)),块链(插入O(sqrt(n)),查询O(1))都行。块链,就是对原数列分块,统计大块的前缀和,对于每个块统计内部元素的前缀和。