https://vjudge.net/problem/CodeChef-FNCS
题意
思路
用分块的方法,对每个函数进行分块,计算出该分块里每个数的个数,这样的话也就能很方便的计算出这个分块里所有数的和。
用树状数组维护数组的话可以很方便的计算出某个区间内所有数的和以及修改某个数。
每次查询时,如果在中间块的函数,我们直接加上sum[i](sum[i]为预处理的每一块的和),对于两边的函数,就用树状数组快速求一下和即可。
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> #include<vector> #include<stack> #include<queue> #include<cmath> #include<map> #include<set> using namespace std; typedef long long ll; typedef pair<int,int> pll; const int INF = 0x3f3f3f3f; const int maxn = 1e5+5; int n , m, unit, num; int a[maxn],l[maxn],r[maxn]; int cnt[400][maxn]; ll c[maxn],sum[maxn]; int lowbit(int x) { return x&-x; } ll getsum(int x) { ll ret = 0; while(x>0) { ret+=c[x]; x-=lowbit(x); } return ret; } int add(int x, int d) { while(x<=n) { c[x]+=d; x+=lowbit(x); } } void update(int x, int y) { add(x,y-a[x]); for(int i=1;i<=num;i++) sum[i]+=(ll)cnt[i][x]*(y-a[x]); a[x]=y; } ll query(int left, int right) { ll ans = 0; int l_unit=(left-1)/unit+1,r_unit=(right-1)/unit+1; if(l_unit==r_unit) { for(int i=left;i<=right;i++) ans+=getsum(r[i])-getsum(l[i]-1); } else { for(int i=l_unit+1;i<r_unit;i++) ans+=sum[i]; for(int i=left;i<=(l_unit)*unit;i++) ans+=getsum(r[i])-getsum(l[i]-1); for(int i=(r_unit-1)*unit+1;i<=right;i++) ans+=getsum(r[i])-getsum(l[i]-1); } return ans; } int main() { //freopen("in.txt","r",stdin); scanf("%d",&n); memset(c,0,sizeof(c)); for(int i=1;i<=n;i++) {scanf("%d",&a[i]);add(i,a[i]);} for(int i=1;i<=n;i++) scanf("%d%d",&l[i],&r[i]); unit = (int)sqrt(n+0.5); num = unit + (unit*unit!=n); int now = 0; for(int i=1;i<=n;i++) { if(i%unit==1) now++; cnt[now][l[i]]++; cnt[now][r[i]+1]--; } for(int i=1;i<=num;i++) { for(int j=1;j<=n;j++) { cnt[i][j]+=cnt[i][j-1]; sum[i]=sum[i]+(ll)cnt[i][j]*a[j]; } } scanf("%d",&m); while(m--) { int op, x, y; scanf("%d%d%d",&op,&x,&y); if(op==1) update(x,y); else printf("%lld ",query(x, y)); } return 0; }