Tido 习题-二叉树-区间查询
题目描述
食堂有N个打饭窗口,现在正到了午饭时间,每个窗口都排了很多的学生,而且每个窗口排队的人数在不断的变化。
现在问你第i个窗口到第j个窗口一共有多少人在排队?
现在问你第i个窗口到第j个窗口一共有多少人在排队?
输入
输入的第一行是一个整数T,表示有T组测试数据。
每组输入的第一行是一个正整数N(N<=30000),表示食堂有N个窗口。
接下来一行输入N个正整数,第i个正整数ai表示第i个窗口最开始有ai个人排队。(1<=ai<=50)
接下来每行有一条命令,命令有四种形式:
(1)Add i j,i和j为正整数,表示第i个窗口增加j个人(j不超过30);
(2)Sub i j,i和j为正整数,表示第i个窗口减少j个人(j不超过30);
(3)Query i j,i和j为正整数,i<=j,表示询问第i到第j个窗口的总人数;
(4)End 表示结束,这条命令在每组数据最后出现;
每组数据最多有40000条命令。
每组输入的第一行是一个正整数N(N<=30000),表示食堂有N个窗口。
接下来一行输入N个正整数,第i个正整数ai表示第i个窗口最开始有ai个人排队。(1<=ai<=50)
接下来每行有一条命令,命令有四种形式:
(1)Add i j,i和j为正整数,表示第i个窗口增加j个人(j不超过30);
(2)Sub i j,i和j为正整数,表示第i个窗口减少j个人(j不超过30);
(3)Query i j,i和j为正整数,i<=j,表示询问第i到第j个窗口的总人数;
(4)End 表示结束,这条命令在每组数据最后出现;
每组数据最多有40000条命令。
输出
对于每组输入,首先输出样例号,占一行。
然后对于每个Query询问,输出一个整数,占一行,表示询问的段中的总人数,这个数保持在int以内。
然后对于每个Query询问,输出一个整数,占一行,表示询问的段中的总人数,这个数保持在int以内。
样例输入 Copy
1
10
1 2 3 4 5 6 7 8 9 10
Query 1 3
Add 3 6
Query 2 7
Sub 10 2
Add 6 3
Query 3 10
End
样例输出 Copy
Case 1:
6
33
59
这一题可以通过线段树来解决
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<string> #include<cstring> using namespace std; const int SIZE=30005; struct SegmentTree{ int l,r; int dat; } t[SIZE*4];//struct数组存储线段树 int a[SIZE]; void build(int p,int l,int r){ t[p].l=l;t[p].r=r;//节点p表示区间l-r if(l==r){ t[p].dat=a[l]; return; }//叶节点 int mid=(l+r)/2;//折半 build(p*2,l,mid);//左子节点[l,mid],编号p*2 build(p*2+1,mid+1,r); //右子节点[mid+1,r],编号p*2+1 t[p].dat=t[p*2].dat+t[p*2+1].dat;//从下往上传递信息 } int ask(int p,int l,int r)//查询区间和 { if(l<=t[p].l&&r>=t[p].r) return t[p].dat; int mid=(t[p].l+t[p].r)/2; int val=0; if(l<=mid) val+=ask(p*2,l,r);//左子节点有重叠 if(r>mid) val+=ask(p*2+1,l,r); //右子节点有重叠 return val; } void change(int p,int x,int v,int xx){//单点修改 if(t[p].l==t[p].r){//找到叶节点 if(xx==1) t[p].dat+=v; if(xx==-1) t[p].dat-=v; return; } int mid=(t[p].l+t[p].r)/2; if(x<=mid) change(p*2,x,v,xx);//x属于左半区间 else change(p*2+1,x,v,xx); //x属于右半区间 t[p].dat=t[p*2].dat+t[p*2+1].dat;//从下往上更新信息 } int main() { int t; cin>>t; for(int i=1;i<=t;i++){ cout<<"Case "<<i<<":"<<endl; int n; cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; build(1,1,n);//建立线段树 string s; while(cin>>s){ if(s=="End") break; int a,b; cin>>a>>b; if(s=="Query") cout<<ask(1,a,b)<<endl; if(s=="Add") change(1,a,b,1); if(s=="Sub") change(1,a,b,-1); } } return 0; }
通过线段树可以快速进行单点更新 和 区间求和
相当于一个非常巧妙的递归
先从上往下到叶节点
再将叶节点往上累加
再从下回到起点
至于线段树的讲解
可以先看一下下面的讲解哦
https://www.cnblogs.com/Tidoblogs/p/10887555.html