洛谷P4868 Preprefix sum
洛谷传送门
题目描述
前缀和(prefix sum)𝑆𝑖=。
前前缀和(preprefix sum)则把 𝑆𝑖 作为原序列再进行前缀和。记再次求得前缀和第 𝑖 个是 𝑆𝑆𝑖。
给一个长度 𝑛 的序列 𝑎1,𝑎2,⋯ ,𝑎𝑛有两种操作:
Modify i x
:把 𝑎𝑖 改成 𝑥。Query i
:查询 𝑆𝑆𝑖。
输入格式
第一行给出两个整数 𝑁,𝑀。分别表示序列长度和操作个数。
接下来一行有 𝑁 个数,即给定的序列 𝑎1,𝑎2,⋯ ,𝑎𝑛。
接下来 𝑀 行,每行对应一个操作,格式见题目描述。
输出格式
对于每个询问操作,输出一行,表示所询问的 𝑆𝑆𝑖 的值。
输入输出样例
输入
5 3 1 2 3 4 5 Query 5 Modify 3 2 Query 5
输出
35 32
说明/提示
1≤𝑁,𝑀≤1e5,且在任意时刻 0≤𝐴𝑖≤1e5。
题目解读
由题意知,题目的意思就是求前缀和的前缀和,下面是一个酣畅淋漓的数学推理
数学推理
举个例子:
//对于 1 2 3 4 5
// a[]=1 2 3 4 5
//s[1]=1
//s[2]=1+2
//s[3]=1+2+3
//s[4]=1+2+3+4
//s[5]=1+2+3+4+5
//ss[5]=s[1]+s[2]+s[3]+s[4]+s[5]
// =1*5++2*(5-1)+3*(5-2)+4*(5-3)+5*(5-4)
依此类推,我们可以发现==
方法
我们用两个树状数组 c 与 c1,分别维护( k 为常数不管),
Code
#include<bits/stdc++.h>
using namespace std;
const long long N=1e5+5;
long long n,m,a[N],c[N],c1[N],id[N],ni[N];
long long lowbit(long long x){return (-x)&x;}
void add(long long x,long long y){for(;x<=n+1;x+=lowbit(x))c[x]+=y;}//将 c[x] 加上 y
void add1(long long x,long long y){for(;x<=n+1;x+=lowbit(x))c1[x]+=y;}//将 c1[x] 加上 y
long long sum(long long x){//求 c 的前缀和
long long ret=0;
for(;x;x-=lowbit(x))ret+=c[x];
return ret;
}
long long sum1(long long x){//求 c1 的前缀和
long long ret=0;
for(;x;x-=lowbit(x))ret+=c1[x];
return ret;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
add(i,a[i]-0);add1(i,(i-1)*(a[i]-0));//分别向 c 和 c1 加入 a[i] 和 (i-1)*a[i]
}
string t;int x,y;
for(int i=1;i<=m;i++){
cin>>t;
if(t=="Query"){
cin>>x;
cout<<x*sum(x)-sum1(x)<<'\n';//输出结果
}
else{
cin>>x>>y;
add(x,y-a[x]);add1(x,(x-1)*(y-a[x]));//修改
a[x]=y;
}
}
return 0;
}