POJ 3468 線段樹
阿新 • • 發佈:2018-12-04
要求:有兩個操作,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); } } }