洛谷 P4868 Preprefix sum
洛谷 P4868 Preprefix sum
题目描述
前缀和(prefix sum)S_i=\sum_{k=1}^i a_kS**i=∑k=1iak。
前前缀和(preprefix sum) 则把S_iS**i作为原序列再进行前缀和。记再次求得前缀和第i个是SS_iSSi
给一个长度n的序列a_1, a_2, \cdots, a_na1,a2,⋯,a**n,有两种操作:
Modify i x
:把a_ia**i改成xx;Query i
:查询SS_iSSi
输入格式
第一行给出两个整数N,M。分别表示序列长度和操作个数
接下来一行有N个数,即给定的序列a1,a2,....an
接下来M行,每行对应一个操作,格式见题目描述
输出格式
对于每个询问操作,输出一行,表示所询问的SSi的值。
题解:
前前缀和化简一下式子就可以推出奇妙的性质。所以本质上其实是数学推导。
但是还需要拿树状数组维护两个不同性质的前缀和。这个其实也并不是很难。
代码:
#include<algorithm>
#include<iostream>
#include<cstdio>
#define int long long
using namespace std;
int n,m,x,y;
const int N=100010;
int ans,len;
int a[N],tr1[N<<1],tr2[N<<1];
char s[101];
int lowbit(int x){return x&(-x);}
void add1(int pos,int x)
{
for(int i=pos;i<=(N<<1);i+=lowbit(i))tr1[i]+=x;
}
void add2(int pos,int x)
{
for(int i=pos;i<=(N<<1);i+=lowbit(i))tr2[i]+=x;
}
int ask1(int pos)
{
int lin=0;
for(int i=pos;i;i-=lowbit(i))lin+=tr1[i];
return lin;
}
int ask2(int pos)
{
int lin=0;
for(int i=pos;i;i-=lowbit(i))lin+=tr2[i];
return lin;
}
signed main()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;++i)
{
scanf("%lld",&a[i]);
add1(i,a[i]);add2(i,a[i]*i);
}
while(m--)
{
scanf("%s",s+1);
if(s[1]=='Q')
{
scanf("%lld",&x);
ans=((x+1)*ask1(x)-ask2(x));
printf("%lld\n",ans);
}
else
{
scanf("%lld%lld",&x,&y);
add1(x,y-a[x]);add2(x,(y-a[x])*x);
a[x]=y;
}
}
return 0;
}