zoukankan      html  css  js  c++  java
  • Tido 习题-二叉树-区间查询

    题目描述

    食堂有N个打饭窗口,现在正到了午饭时间,每个窗口都排了很多的学生,而且每个窗口排队的人数在不断的变化。
    现在问你第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条命令。

    输出

    对于每组输入,首先输出样例号,占一行。
    然后对于每个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
  • 相关阅读:
    [Bzoj2152]聪聪可可
    [2019杭电多校第七场][hdu6655]Just Repeat
    [2019杭电多校第七场][hdu6651]Final Exam
    [2019杭电多校第七场][hdu6646]A + B = C(hash)
    [2019杭电多校第六场][hdu6641]TDL
    [2019杭电多校第六场][hdu6638]Snowy Smile(维护区间最大子段和)
    abc179f
    Codeforces Round #680A
    Codeforces Round #680B
    Codeforces Round #681 D
  • 原文地址:https://www.cnblogs.com/Tidoblogs/p/10887599.html
Copyright © 2011-2022 走看看