链接地址:here
题意:
给 N (
≤105 )个数,给定 N 个函数,每个函数为序列中第Li 到第 Ri 个数的和,有 Q (≤105 )个询问,有两种类型的操作:
- 1 x
y 把序列中的第 x 个数改为y - 2 m
n 求第 m 个函数到第n 个函数的和
思路:
分块+树状数组
对N个函数进行分块,分块处理出每个块内,序列中第i个数被这个块内的函数覆盖的次数, 然后处理出每个块内函数的和,由于是单点更新,所以在维护每个块函数的和的时候是比较好维护的(因为已经知道了第i个数被这个块内的函数覆盖的次数),然后就是区间查询了,直接分块处理就好了,块内求解的时候就需要用到树状数组去优化了。分块大法好!1240 ms 跑过,内存消耗太大啊!
代码:
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <string>
#define PB push_back
#define FT first
#define SD second
#define MP make_pair
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> P;
const int N=5+1e5,M=400,MOD=7+1e9;
int n, a[N];
P d[N];
int belong[N],l[M],r[M];
int vis[M][N];
ULL sum[N],sumb[N];
void update(int pos,int val)
{
while(pos<N) {
sum[pos] += val;
pos += pos&(-pos);
}
}
ULL read(int pos)
{
ULL ans=0;
while(pos) {
ans += sum[pos];
pos -= pos&(-pos);
}
return ans;
}
ULL cal(int x)
{
if(x<1) return 0;
int b = belong[x];
ULL ans = 0;
for(int i = 1;i < b ;i ++){
ans += sumb[i];
}
for(int i = l[b];i <= x;i ++) {
ans += read(d[i].SD)-read(d[i].FT-1);
}
return ans;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
scanf("%d",&n);
for(int i = 1;i <= n;i ++) {
scanf("%d",&a[i]);
update(i,a[i]);
}
int block = sqrt(n);
for(int i = 1;i <= n;i ++) {
scanf("%d%d",&d[i].FT,&d[i].SD);
belong[i] = (i-1)/block + 1;
}
int cnt = n/block;
if(n%block) cnt++;
for(int i = 1;i <= cnt;i ++)
l[i] = (i-1)*block + 1,r[i] = i*block;
r[cnt] = n;
for(int i = 1;i <= cnt;i ++){
for(int j = l[i];j <= r[i];j ++) {
vis[i][d[j].FT] ++;
vis[i][d[j].SD+1] --;
}
for(int j = 1;j <= n;j ++) {
vis[i][j] += vis[i][j-1];
sumb[i] += 1ULL*vis[i][j]*a[j];
}
}
int Q;
scanf("%d",&Q);
while(Q-- ){
int tp,x,y;
scanf("%d%d%d",&tp,&x,&y);
if(tp == 1) {
update(x,y-a[x]);
for(int i = 1;i <= cnt;i ++){
sumb[i] += 1ULL*vis[i][x]*(y-a[x]);
}
a[x] = y;
}
else {
printf("%llu\n",cal(y)-cal(x-1));
}
}
return 0;
}