基础线段树——讲解+例题

线段树讲解

初级  https://blog.csdn.net/zearot/article/details/52280189  

进阶  http://www.cnblogs.com/AC-King/p/7789013.html

主席树(权值线段树) https://blog.csdn.net/g21wcr/article/details/82970228

HDU - 1166 

C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了。A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务就是要监视这些工兵营地的活动情况。由于采取了某种先进的监测手段,所以每个工兵营地的人数C国都掌握的一清二楚,每个工兵营地的人数都有可能发生变动,可能增加或减少若干人手,但这些都逃不过C国的监视。 
中央情报局要研究敌人究竟演习什么战术,所以Tidy要随时向Derek汇报某一段连续的工兵营地一共有多少人,例如Derek问:“Tidy,马上汇报第3个营地到第10个营地共有多少人!”Tidy就要马上开始计算这一段的总人数并汇报。但敌兵营地的人数经常变动,而Derek每次询问的段都不一样,所以Tidy不得不每次都一个一个营地的去数,很快就精疲力尽了,Derek对Tidy的计算速度越来越不满:"你个死肥仔,算得这么慢,我炒你鱿鱼!”Tidy想:“你自己来算算看,这可真是一项累人的工作!我恨不得你炒我鱿鱼呢!”无奈之下,Tidy只好打电话向计算机专家Windbreaker求救,Windbreaker说:“死肥仔,叫你平时做多点acm题和看多点算法书,现在尝到苦果了吧!”Tidy说:"我知错了。。。"但Windbreaker已经挂掉电话了。Tidy很苦恼,这么算他真的会崩溃的,聪明的读者,你能写个程序帮他完成这项工作吗?不过如果你的程序效率不够高的话,Tidy还是会受到Derek的责骂的. 

Input

第一行一个整数T,表示有T组数据。 
每组数据第一行一个正整数N(N<=50000),表示敌人有N个工兵营地,接下来有N个正整数,第i个正整数ai代表第i个工兵营地里开始时有ai个人(1<=ai<=50)。 
接下来每行有一条命令,命令有4种形式: 
(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条命令 

Output

对第i组数据,首先输出“Case i:”和回车, 
对于每个Query询问,输出一个整数并回车,表示询问的段中的总人数,这个数保持在int以内。 

Sample Input

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 

Sample Output

Case 1:
6
33
59

线段树改点求点

//struct A
//{
//    int x,y;
//    bool operator < (const A & a) const
//    {
//        return x>a.x;
//    }
//}; //优先队列(按结构体中的x从小到大排序)
//priority_queue <A> q;
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <deque>
#include <stack>
#include <map>
#include <set>
#define eps 0.0000000001
#define mem(a) memset(a,0,sizeof(a))
#define maxx 1e10
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
//priority_queue<int,vector<int>,greater<int> > q;
#define mod 1000000007
const int maxn=50100;
int a[maxn];
int sum[maxn*4];
//建树
void Build(int l,int r,int num)
{
    if(l==r)
    {
        sum[num]=a[l];
        return;
    }
    int m=(l+r)/2;
    //左右递归
    Build(l,m,num*2);
    Build(m+1,r,num*2+1);
    //该结点值等于左右孩子结点值的和
    sum[num]=sum[num*2]+sum[num*2+1];
    return;
}

//点更新  假设a[x]+=s;
void Update(int x,int s,int l,int r,int num)
{
    if(l==r)
    {
        sum[num]+=s;
        return;
    }
    int m=(l+r)/2;
    //根据m和x的位置关系判断调用左子树还是右子树
    if(x<=m)//左
        Update(x,s,l,m,num*2);
    else
        Update(x,s,m+1,r,num*2+1);
    sum[num]=sum[num*2]+sum[num*2+1];
}

//区间查询  查询区间 L,R
int Query(int L,int R,int l,int r,int num)
{
    if(L<=l&&R>=r) //l,r在L,R内
    {
        return sum[num];
    }
    int m=(l+r)/2;
    int ans=0;
    if(L<=m)
        ans+=Query(L,R,l,m,num*2);
    if(R>=m+1)
        ans+=Query(L,R,m+1,r,num*2+1);
    return ans;
}

int main()
{
    int T;cin>>T;
    for(int w=1;w<=T;w++)
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        Build(1,n,1);
        cout<<"Case "<<w<<":"<<endl;
        string s;
        while(cin>>s)
        {
            if(s[0]=='E')
                break;
            else if(s[0]=='A')
            {
                int x,y;
                scanf("%d %d",&x,&y);
                Update(x,y,1,n,1);
            }
            else if(s[0]=='S')
            {
                int x,y;
                scanf("%d %d",&x,&y);
                y=(-1)*y;
                Update(x,y,1,n,1);
            }
            else if(s[0]=='Q')
            {
                int L,R;
               scanf("%d %d",&L,&R);
                cout<<Query(L,R,1,n,1)<<endl;
            }
        }

    }
}

2.HDU - 1556 

N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的“小飞鸽"牌电动车从气球a开始到气球b依次给每个气球涂一次颜色。但是N次以后lele已经忘记了第I个气球已经涂过几次颜色了,你能帮他算出每个气球被涂过几次颜色吗?

Input

每个测试实例第一行为一个整数N,(N <= 100000).接下来的N行,每行包括2个整数a b(1 <= a <= b <= N)。 
当N = 0,输入结束。

Output

每个测试实例输出一行,包括N个整数,第I个数代表第I个气球总共被涂色的次数。

Sample Input

3
1 1
2 2
3 3
3
1 1
1 2
1 3
0

Sample Output

1 1 1
3 2 1

线段树改段求点

//struct A
//{
//    int x,y;
//    bool operator < (const A & a) const
//    {
//        return x>a.x;
//    }
//}; //优先队列(按结构体中的x从小到大排序)
//priority_queue <A> q;
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <deque>
#include <stack>
#include <map>
#include <set>
#define eps 0.0000000001
#define mem(a) memset(a,0,sizeof(a))
#define maxx 1e10
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
//priority_queue<int,vector<int>,greater<int> > q;
#define mod 1000000007
const int maxn=100100;
int a[maxn];
int sum[maxn*4];
int Add[maxn*4];//懒惰标记
void PushUp(int n)
{
    sum[n]=sum[n*2]+sum[n*2+1];
    return;
}
//建树
void Build(int l,int r,int num)
{
    if(l==r)
    {
        sum[num]=a[l];
        return;
    }
    int m=(l+r)/2;
    //左右递归
    Build(l,m,num*2);
    Build(m+1,r,num*2+1);
    //该结点值等于左右孩子结点值的和
    PushUp(num);
    return;
}

//点更新  假设a[x]+=s;
void Update1(int x,int s,int l,int r,int num)
{
    if(l==r)
    {
        sum[num]+=s;
        return;
    }
    int m=(l+r)/2;
    //根据m和x的位置关系判断调用左子树还是右子树
    if(x<=m)//左
        Update1(x,s,l,m,num*2);
    else
        Update1(x,s,m+1,r,num*2+1);
    PushUp(num);
}

// 下推标记函数
void PushDown(int num,int ln,int rn)//ln和rn是左子树和右子树的树叶的数量
{
    if(Add[num]) //如果被标记了
    {
        Add[num*2]+=Add[num];
        Add[num*2+1]+=Add[num];// 把标记推到左右子树
        //修改子节点的Sum使之与对应的Add相对应
        sum[num*2]+=Add[num]*ln;
        sum[num*2+1]+=Add[num]*rn;
        Add[num]=0;
    }
    return;
}

//区间更新  假设L到R 加C
void Update2(int L,int R,int C,int l,int r,int num)
{
    if(l>=L&&R>=r)
    {
        sum[num]+=C*(r-l+1);
        Add[num]+=C;//增加Add标记,表示本区间的Sum正确,子区间的Sum仍需要根据Add的值来调整
        return;
    }
    int m=(l+r)/2;
    PushDown(num,m-l+1,r-m);  //下推标记
    if(L<=m)
        Update2(L,R,C,l,m,num*2);
    if(R>=m+1)
        Update2(L,R,C,m+1,r,num*2+1);
    PushUp(num);
    return;
}

//区间查询  查询区间 L-R
int Query(int L,int R,int l,int r,int num)
{
    if(L<=l&&R>=r) //l,r在L,R内
    {
        return sum[num];
    }
    int m=(l+r)/2;
    PushDown(num,m-l+1,r-m);
    int ans=0;
    if(L<=m)
        ans+=Query(L,R,l,m,num*2);
    if(R>=m+1)
        ans+=Query(L,R,m+1,r,num*2+1);
    return ans;
}

int main()
{
    int n;
    while(cin>>n&&n)
    {
        mem(a);mem(sum);mem(Add);
        //Build(1,n,1);
        for(int i=0;i<n;i++)
        {
            int x,y;
            cin>>x>>y;
            Update2(x,y,1,1,n,1);
        }
        for(int i=1;i<=n;i++)
        {
            if(i==1)
                cout<<Query(i,i,1,n,1);
            else
                cout<<" "<<Query(i,i,1,n,1);
        }
        cout<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Septembre_/article/details/88624334
今日推荐