ACM-ICPC 2018徐州赛区网络预赛 H.Ryuji doesn't want to study(线段树)

题目

n个数,q个操作(n,q<=1e5)

操作分两种,

1 l r 询问[l,r]区间内,\sum _{i=1}^{r-l+1}i*a[r-i+1]的值

2 x y 把a[x]的值改成y

思路来源

凯神

题解

结构体维护三个值

sum:这一段的和 ans:这一段的答案 num:这一段的数字的个数

3a_{1}+2a_{2}+a_{3}2a_{4}+a_{5}合并的时候

sum和num可以直接合并,

而这一段的ans

ans[p]=5a_{1}+4a_{2}+3a_{3}+2a_{4}+a_{5}

=(3a_{1}+2a_{2}+a_{3})+2a_{4}+a_{5}+(2a_{1}+2a_{2}+2a_{3})

=(3a_{1}+2a_{2}+a_{3})+2a_{4}+a_{5}+(a_{1}+a_{2}+a_{3})*2

=ans[p<<1]+ans[p<<1|1]+sum[p<<1]*num[p<<1|1]

心得

还是结构体的比较常用吖

配合函数传参这种的比较好写

代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll; 
const int maxn=1e5+10;
int n,q,a[maxn];
struct node
{
	ll sum,ans,num;
	node():sum(0),ans(0),num(0){
	} 
	//sum:al+...+ar ans:l*al+...+ar num:r-l+1 
}e[maxn*5];
node cal(node a,node b)
{
		node ans;
		ans.sum=a.sum+b.sum;
		ans.num=a.num+b.num;
		ans.ans=a.ans+b.ans+a.sum*b.num;
		return ans;
}
void pushup(int p)
{
	e[p]=cal(e[p<<1],e[p<<1|1]);
}
void build(int p,int l,int r)
{
	if(l==r)
	{
		e[p].sum=e[p].ans=a[l];
		e[p].num=1;
		return;
	}
	int mid=(l+r)/2;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	pushup(p);
}
void update(int p,int l,int r,int x,int v)
{
	if(l==r)
	{
		e[p].sum=e[p].ans=v;
		return;
	}
	int mid=(l+r)/2;
	if(x<=mid)update(p<<1,l,mid,x,v);
	else update(p<<1|1,mid+1,r,x,v);
	pushup(p);
} 
node ask(int p,int l,int r,int ql,int qr)
{
	if(ql<=l&&r<=qr)return e[p];
	int mid=(l+r)/2;
	node L,R,ans;
	if(ql<=mid)L=ask(p<<1,l,mid,ql,qr);
	if(qr>mid)R=ask(p<<1|1,mid+1,r,ql,qr);
	ans=cal(L,R);
	return ans;
}
int main()
{
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;++i)
	scanf("%d",&a[i]); 
	build(1,1,n);
	while(q--)
	{
		int op,x,y;
		scanf("%d%d%d",&op,&x,&y);
		if(op==1)printf("%lld\n",ask(1,1,n,x,y).ans);
		else if(op==2)update(1,1,n,x,y);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Code92007/article/details/89066475