浏览器标题切换
浏览器标题切换end

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 }
View Code

 

posted @ 2019-08-01 22:01  抓水母的派大星  阅读(139)  评论(0编辑  收藏  举报