洛谷 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,有两种操作:

  1. Modify i x:把a_ia**i改成xx
  2. 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
",ans);
		}
		else
		{
			scanf("%lld%lld",&x,&y);
			add1(x,y-a[x]);add2(x,(y-a[x])*x);
			a[x]=y;
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/fusiwei/p/14031281.html