1. 程式人生 > >POJ 3468 線段樹

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);
		}
	 }
}