Big Water Problem (线段树)

Big Water Problem

题目描述

给一个数列,会有多次询问,对于每一次询问,会有两种操作:
1:给定两个整数x, y, 然后在原数组的第x位置上加y;
2:给定两个整数l,r,然后输出数组从第l位加到第r位数字的和并换行

输入描述:

第一行有两个整数n, m(1 <= n, m <= 100000)代表数列的长度和询问的次数
第二行n个数字,对于第i个数字a[i],(0<=a[i]<=100000)。
接下来m行,每一行有三个整数f, x, y。第一个整数f是1或者是2,代表操作类型,如果是1,接下来两个数x,y代表第x的位置上加y,如果是2,则求x到y的和,保证数据合法。

输出描述:

输出每次求和的结果并换行
示例1

输入

10 2
1 2 3 4 5 6 7 8 9 10
1 1 9
2 1 10

输出

64



思路:线段树的单点修改加区间查询......


  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<cmath>
  5 #include<algorithm>
  6 #include<map>
  7 #include<set>
  8 #include<vector>
  9 using namespace std;
 10 #define ll long long
 11 const int inf=99999999;
 12 const int mod=1e9+7;
 13 const int maxn=100000+10;
 14 typedef struct 
 15 {
 16     int left;
 17     int right;
 18     ll int weight;
 19     ll int lan;
 20 } St;
 21 St tree[maxn<<2];
 22 int n,m;
 23 int a,b;
 24 ll int ans;
 25 ll int num; 
 26 inline void build_tree(int k,int left,int right)//建树 
 27 {
 28     tree[k].left=left;
 29     tree[k].right=right;
 30     if(left==right)
 31     {
 32         cin>>tree[k].weight;
 33         return ;
 34     }
 35     int mid=(left+right)>>1;
 36     build_tree(k<<1,left,mid);//左子树
 37     build_tree(k<<1|1,mid+1,right);//右子树
 38     tree[k].weight=tree[k<<1].weight+tree[k<<1|1].weight;//状态合并,该节点权重等于左子树加右子树权重 
 39 }
 40 inline void lan_down(int k)//树懒标记下传 
 41 {
 42     tree[k<<1].lan+=tree[k].lan;
 43     tree[k<<1|1].lan+=tree[k].lan;
 44     tree[k<<1].weight+=tree[k].lan*(tree[k<<1].right-tree[k<<1].left+1);
 45     tree[k<<1|1].weight+=tree[k].lan*(tree[k<<1|1].right-tree[k<<1|1].left+1);
 46     tree[k].lan=0;
 47 }
 48 inline void ask_point(int k)//单点查询 
 49 {
 50     if(tree[k].left==tree[k].right)
 51     {
 52         ans=tree[k].weight;
 53         return ;
 54     }
 55     if(tree[k].lan)
 56         lan_down(k);
 57     int mid=(tree[k].left+tree[k].right)>>1;
 58     if(a<=mid)//a表示单点询问位置 
 59         ask_point(k<<1);//目标位置靠左,递归左孩子
 60     else
 61         ask_point(k<<1|1);//目标位置靠右,递归右孩子
 62 }
 63 inline void add_point(int k)//单点修改 
 64 {
 65     if(tree[k].left==tree[k].right)
 66     {
 67         tree[k].weight+=num;//单点增加num 
 68         return ;
 69     }
 70     if(tree[k].lan)//树懒标记下传 
 71         lan_down(k);
 72     int mid=(tree[k].left+tree[k].right)>>1;
 73     if(a<=mid)
 74         add_point(k<<1);
 75     else
 76         add_point(k<<1|1);
 77     tree[k].weight=tree[k<<1].weight+tree[k<<1|1].weight;//状态合并 
 78 }
 79 inline void ask_qujian(int k)//区间查询 
 80 {
 81     if(tree[k].left>=a&&tree[k].right<=b)//包含了left,right当前区间,全加上 
 82     {
 83         ans+=tree[k].weight;
 84         return ;
 85     }
 86     if(tree[k].lan)//树懒标记下传 
 87         lan_down(k);
 88     int mid=(tree[k].left+tree[k].right)>>1;
 89     if(a<=mid)//递归查询左子区间 
 90         ask_qujian(k<<1);
 91     if(b>mid)//递归查询右子区间
 92         ask_qujian(k<<1|1);
 93 }
 94 inline void add_qujian(int k)//区间修改 
 95 {
 96     if(tree[k].left>=a&&tree[k].right<=b)
 97     {
 98         tree[k].weight+=num*(tree[k].right-tree[k].left+1);
 99         tree[k].lan+=num;
100         return ;
101     }
102     if(tree[k].lan)
103         lan_down(k);
104     int mid=(tree[k].left+tree[k].right)>>1;
105     if(a<=mid)
106         add_qujian(k<<1);
107     if(b>mid)
108         add_qujian(k<<1|1);
109     tree[k].weight=tree[k<<1].weight+tree[k<<1|1].weight;//状态合并 
110 }
111 int main()
112 {
113     ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
114     cin>>n>>m; //n个节点,m个操作 
115     build_tree(1,1,n);//建树
116     int op;
117     for(int i=0;i<m;i++)
118     {
119         cin>>op; 
120         if(op&1)
121         {
122             cin>>a>>num;
123             add_point(1);//单点修改 
124         }
125         else
126         {
127             cin>>a>>b;
128             ans=0;
129             ask_qujian(1);
130             cout<<ans<<endl;
131         }
132     }
133     return 0;
134 }



猜你喜欢

转载自www.cnblogs.com/xwl3109377858/p/10917505.html