poj-3468-A Simple Problem with Integers-线段树入门+区间更新
题意:
C:对区间[l,r]每一个数+c;
Q:查询区间[l,r]的所有元素的总和。
线段树修改和查找的时间复杂度都是O(logn)。
线段树基本思想:分治。
线段树基本操作:建树、区间查询(最值;和)、区间修改(更新)、单点修改、单点查询。
注意这题,输入说是一行 N、q 单组输入,但是会TLE,多组输入才可以AC。
AC代码:
1 //题意: 2 //C:对区间[l,r]每一个数+c; 3 // Q:查询区间[l,r]的所有元素的总和。 4 5 //线段树修改和查找的时间复杂度都是O(logn)。 6 //线段树基本思想:分治。 7 //线段树基本操作:建树、区间查询(最值;和)、区间修改(更新)、单点修改、单点查询。 8 9 #include<stdio.h> 10 #include<string.h> 11 #include<iostream> 12 #include<queue> 13 #include<map> 14 #include<stack> 15 #include<queue> 16 #include<algorithm> 17 #include<cmath> 18 19 using namespace std; 20 #define inf 0x3f3f3f3f; 21 #define pi acos(-1.0) 22 #define mem(a,b) memset(a,b,sizeof(a)) 23 #define eps 1e-9 24 typedef long long ll; 25 26 const int N=1e5+20; 27 ll a[N<<2],lazy[N<<2];//需要开到节点的四倍大小 28 29 void build(int L,int R,int i) 30 { 31 if(L==R)//当左右结点相同的时候,说明该节点可以建树,输入即可。 32 { 33 scanf("%lld",&a[i]);//即为叶子结点 34 return;//因为已经确定这个点可以输入了,也就类似叶结点,返回函数上次调用的地方即可。 35 } 36 37 //否则往下继续找 38 int mid=(L+R)>>1; 39 build(L,mid,i<<1);//递归建立左子树 40 build(mid+1,R,i<<1|1);//递归建立右子树 41 a[i]=a[i<<1]+a[i<<1|1];//统计该点(i)的左子树和右子树之和 42 //a这个操作也可以另外写到一个函数pushup中(即pushup(i)),这个看自己怎么写代码 43 //节点数据向上更新 44 45 //根据题意写,这一题是求区间和,之前左区间和右区间相加即可 46 //例如如果求区间内最大值,则写成:a[i]=max(a[i<<1],a[i<<1|1]); 47 } 48 49 void pushdown(int i,int len)//节点懒惰标记下推 50 { 51 if(lazy[i])//如果懒惰标记为真,说明之前有过懒惰标记,现在需要进行更新 52 { 53 lazy[i<<1]+=lazy[i];//懒惰标记往左结点传 54 lazy[i<<1|1]+=lazy[i];//懒惰标记往右结点传 55 //左右用 |1 区分 56 //因为求区间和,所以当区间内每个元素加上一个值时,区间的和也加上这个值 57 //对于区间求和, 原数组值需要加上lazy标记*子树所统计的区间长度 58 a[i<<1]+=lazy[i]*(len-(len>>1));//(len-(len>>1)是左区间的长度 59 a[i<<1|1]+=lazy[i]*(len>>1);//(len>>1)是右区间的长度 60 lazy[i]=0;//由于懒惰标记向下传递,所以当前节点的懒惰标记取消 61 } 62 //对于区间求最大值, 子树的值不需要乘以长度, 所以不需要传递参数区间长度len。 63 } 64 65 //注意: 66 // 1、单点更新, 不需要用到lazy标记 67 // 2、成段(区间)更新, 需要用到lazy标记来提高时间效率 68 void update(int x,int y,int L,int R,int i,int pluss) 69 { 70 if(L>=x&&R<=y)//当前节点区间包含在查询区间内 71 //范围缩小到left和right之间 72 { 73 a[i]+=pluss*(R-L+1); 74 lazy[i]+=pluss; 75 return; 76 } 77 pushdown(i,R-L+1); 78 int mid=(L+R)>>1; 79 80 //更新区间 81 if(x<=mid)//更新左区间 82 update(x,y,L,mid,i<<1,pluss); 83 if(y>mid)//更新右区间 84 update(x,y,mid+1,R,i<<1|1,pluss); 85 86 //更新结点值 87 a[i]=a[i<<1]+a[i<<1|1]; 88 } 89 90 ll query(int x,int y,int L,int R,int i)//查询操作 91 { 92 if(L>=x&&R<=y)//当前节点区间包含在查询区间内 93 return a[i];//返回当前值 94 pushdown(i,R-L+1); 95 int mid=(L+R)>>1; 96 ll ans=0; 97 if(x<=mid)//递归查询左子树内部的的区间值 98 ans+=query(x,y,L,mid,i<<1); 99 if(y>mid)//递归查询右子树内部的的区间值 100 ans+=query(x,y,mid+1,R,i<<1|1); 101 return ans;//返回题目所需的区间和(左+右) 102 } 103 104 int main() 105 { 106 int n,q; 107 while(~scanf("%d %d",&n,&q)) 108 { 109 mem(lazy,0);//如果多组数据lazy数组需要进行清空 110 mem(a,0); 111 build(1,n,1);//开始建树,传入树的总区间(传入最左端点,最右端点)和树的根节点 112 //建树的过程中输入每一个节点 113 for(int i=1;i<=q;i++) 114 { 115 char ch; 116 getchar();//吸收每次读入的空格 117 scanf("%c",&ch); 118 if(ch=='Q')//询问区间内的和 119 { 120 int x,y; 121 scanf("%d %d",&x,&y); 122 ll ans=query(x,y,1,n,1); 123 printf("%lld\n",ans); 124 }else if(ch=='C')//往区间内每一个数上都插入pluss 125 { 126 int x,y,z; 127 scanf("%d %d %d",&x,&y,&z); 128 update(x,y,1,n,1,z); 129 } 130 } 131 } 132 return 0; 133 }