POJ 3468 线段树

要求:有两个操作,Q是询问一段区间的和,C是将一段区间的所有数加上一个数。

方法:线段树模板题。

1.定义addv[i]为第i个结点对应的区间加上addv[i],但是先不将addv[i]下放给子孙,这样可以减小时间复杂度。

2.定于sumv[i]为第i个结点对应的区间的和。

3.pushdown函数将addv[i]下方给第i个结点的孩子,并更新子孙的sumv数组。

4.pushup函数更新父亲的sumv。

5.update数组更新addv,下放pushdown和上去pushup不会更新所有的结点。

6.由5知查询时需继续进行pushdown,无需进行pushup。

#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
using namespace std;
int k,n,Q,a,b,c;
long long add,sumv[1000005],addv[1000005],sum1;
void pushdown(int i,int ln,int rn)
{
	sumv[i*2]+=addv[i]*ln;
	sumv[i*2+1]+=addv[i]*rn;
	addv[i*2]+=addv[i];
	addv[i*2+1]+=addv[i];
	addv[i]=0;
}
void pushup(int i)
{
	sumv[i]=sumv[i*2]+sumv[i*2+1];
}
void query(int i,int L,int R)
{
	int M=L+(R-L)/2;
	if(a<=L&&R<=b)
	  sum1+=sumv[i];
	else
	{
	  pushdown(i,M-L+1,R-M);//下去add 
	  if(a<=M) query(i*2,L,M);
	  if(M<b)  query(i*2+1,M+1,R);
	}
}
void update(int i,int L,int R)
{
	int lc=i*2,rc=i*2+1;
	int M=L+(R-L)/2;
	if(a<=L&&R<=b)//addv 存的是区间+add 
	{
	  sumv[i]+=c*(R-L+1);
	  addv[i]+=c;	
	  return;
	}
	pushdown(i,M-L+1,R-M);//下去add
	if(a<=M) update(lc,L,M);
	if(b>M) update(rc,M+1,R);
	pushup(i);//下去add	
}
int main()
{
   int i; 
   char s;
   scanf("%d%d",&n,&Q);
   	 memset(sumv,0,sizeof(sumv));
   	 memset(addv,0,sizeof(addv));
	 for(i=1;i<=n;i++)
   	 {
   	   scanf("%d",&c);
   	   a=b=i;
   	   update(1,1,n);
	 }
   	 memset(addv,0,sizeof(addv));
	 for(i=0;i<Q;i++)
	 {
	 	getchar();
	 	scanf("%c",&s);
	 	if(s=='Q')
	 	{
	 	 scanf("%d%d",&a,&b);
	 	 sum1=0;
	 	 query(1,1,n);
	 	 printf("%lld\n",sum1);		
		}
		else
		{
		  scanf("%d%d%d",&a,&b,&c);	
		  update(1,1,n);
		}
	 }
}

猜你喜欢

转载自blog.csdn.net/Irving0323/article/details/83120219