Vision_数据结构_线段树

///定义:
/*
    线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,
每个单元区间对应线段树中的一个叶结点。使用线段树可以快速的查找某一个节点在
若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,实
际应用时一般还要开4N的数组以免越界,因此有时需要离散化让空间压缩。
*/

///代码:

/*
**name:一维线段树
**function:一维线段树的最值、区间和,区间更新
*/
#include <bits/stdc++.h>
#define MAXN 100010
#define inf 0x3f3f3f3f
using namespace std;

struct node{
    int l,r;//区间[l,r]
    int add;//区间的延时标记
    int sum;//区间和
    int mx; //区间最大值
    int mn; //区间最小值
}tree[MAXN<<2];//一定要开到4倍多的空间

void pushup(int index){
    tree[index].sum = tree[index<<1].sum+tree[index<<1|1].sum;
    tree[index].mx = max(tree[index<<1].mx,tree[index<<1|1].mx);
    tree[index].mn = min(tree[index<<1].mn,tree[index<<1|1].mn);
}
void pushdown(int index){
    //说明该区间之前更新过
    //要想更新该区间下面的子区间,就要把上次更新该区间的值向下更新
    if(tree[index].add > 0){
        //替换原来的值

        tree[index<<1].sum += (tree[index<<1].r-tree[index<<1].l+1)*tree[index].add;
        tree[index<<1|1].sum +=(tree[index<<1|1].r-tree[index<<1|1].l+1)*tree[index].add;
        tree[index<<1].mx += tree[index].add;
        tree[index<<1|1].mx += tree[index].add;
        tree[index<<1].mn += tree[index].add;
        tree[index<<1|1].mn += tree[index].add;
        tree[index<<1].add += tree[index].add;
        tree[index<<1|1].add += tree[index].add;
        tree[index].add = 0;

    }
}
void build(int l,int r,int index){
    tree[index].l = l;
    tree[index].r = r;
    tree[index].add = 0;//刚开始一定要清0
    if(l == r){
        scanf("%d",&tree[index].sum);
        tree[index].mn = tree[index].mx = tree[index].sum;
        return ;
    }
    int mid = (l+r)>>1;
    build(l,mid,index<<1);
    build(mid+1,r,index<<1|1);
    pushup(index);
}
void updata(int l,int r,int index,int val){
    if(l <= tree[index].l && r >= tree[index].r){
        /*把原来的值替换成val,因为该区间有tree[index].r-tree[index].l+1
        个数,所以区间和 以及 最值为:
        */
        /*tree[index].sum = (tree[index].r-tree[index].l+1)*val;
        tree[index].mn = val;
        tree[index].mx = val;
        tree[index].add = val;//延时标记*/
        //在原来的值的基础上加上val,因为该区间有tree[index].r-tree[index].l+1
        //个数,所以区间和 以及 最值为:
        tree[index].sum += (tree[index].r-tree[index].l+1)*val;
        tree[index].mn += val;
        tree[index].mx += val;
        tree[index].add += val;//延时标记

        return ;
    }
    pushdown(index);
    int mid = (tree[index].l+tree[index].r)>>1;
    if(l <= mid){
        updata(l,r,index<<1,val);
    }
    if(r > mid){
        updata(l,r,index<<1|1,val);
    }
    pushup(index);
}
int query(int l,int r,int index){
    if(l <= tree[index].l && r >= tree[index].r){
        //return tree[index].sum;
        return tree[index].mx;
        //return tree[index].mn;
    }
    pushdown(index);
    int mid = (tree[index].l+tree[index].r)>>1;
    int ans = 0;
    int Max = 0;
    int Min = inf;
    if(l <= mid){
        ans += query(l,r,index<<1);
        Max = max(query(l,r,index<<1),Max);
        Min = min(query(l,r,index<<1),Min);
    }
    if(r > mid){
        ans += query(l,r,index<<1|1);
        Max = max(query(l,r,index<<1|1),Max);
        Min = min(query(l,r,index<<1|1),Min);
    }
    //return ans;
    return Max;
    //return Min;
}
int main()
{
    int n,m,q,x,y,z;
    while(~scanf("%d%d",&n,&m)){
        build(1,n,1);
        while(m--){
            scanf("%d",&q);
            if(q == 1){
                cout<<"查询:(x,y)"<<endl;
                scanf("%d %d",&x,&y);
                cout<<query(x,y,1)<<endl;
            }
            else{
                cout<<"更新(x,y)为z:"<<endl;
                scanf("%d %d %d",&x,&y,&z);
                updata(x,y,1,z);
                for(int i = 1; i <= n; ++i){
                    printf("a[%d] = %d\n",i,query(i,i,1));
                }
            }
        }
    }
    return 0;
}

/*
**name:一维线段树
**function:一维线段树的区间翻转
**题意:给出N个数,要求做M次区间翻转(如1 2 3 4变成4 3 2 1),求出最后的序列
。用线段树维护区间更改。线段树的叶子节点存每个位置是原序列中的第几号元素。
然后维护当前点的编号和每个点的左右儿子的序号,反转区间时,交换左右儿子即可,不过要注意,
每次打标记的时候,不能直接把delta置1(防止覆盖原标记),用异或取反
*/
///(注意,这样做是有BUG的,只有在交换区间正好是线段树中的整区间才行。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int tree[6000010][3],delta[6000010];
int n,m,a[150010],num[150010];
inline void swap(int now)
{
    int t;
    t=tree[now][1];
    tree[now][1]=tree[now][2];
    tree[now][2]=t;
}
inline void updata(int now)
{
    tree[now][0]=0;
    tree[now][1]=(now<<1);
    tree[now][2]=(now<<1)|1;
}
inline void pushdown(int now)
{
    if(delta[now])
    {
        swap(now<<1);
        swap(now<<1|1);
        delta[now<<1]^=1;
        delta[now<<1|1]^=1;
        delta[now]=0;
    }
}
void build(int now,int l,int r)
{
    if(l==r)
    {
        tree[now][0]=l;
        return;
    }
    int mid=(l+r)>>1;
    build((now<<1),l,mid);
    build((now<<1)|1,mid+1,r);
    updata(now);
}
void change(int now,int l,int r,int al,int ar)
{
    if(al<=l&&r<=ar)
    {
        swap(now);
        delta[now]^=1;
        return;
    }
    int mid=(l+r)>>1;
    pushdown(now);
    if(al<=mid) change(tree[now][1],l,mid,al,ar);
    if(ar>mid) change(tree[now][2],mid+1,r,al,ar);
}
int ask(int now,int l,int r,int al,int ar)
{
    if(al<=l&&r<=ar) return tree[now][0];
    int mid=(l+r)>>1;
    pushdown(now);
    if(al<=mid) return ask(tree[now][1],l,mid,al,ar);
    if(ar>mid) return ask(tree[now][2],mid+1,r,al,ar);
}
int main()
{
    int i,j;
    scanf("%d",&n);
    for(i=1; i<=n; ++i) scanf("%d",&a[i]);
    build(1,1,n);
    scanf("%d",&m);
    for(i=1; i<=m; ++i)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        change(1,1,n,x,y);
    }
    for(j=1; j<=n; ++j) num[j]=ask(1,1,n,j,j);
    for(j=1; j<=n; ++j) printf("%d ",a[num[j]]);
    return 0;
}


/*
**name:二维线段树
**function:二维线段树区间翻转
**题意:有一个矩阵,初始全是0,然后对区间左上角(x1,y1)-右下角(y1,y2)翻转
0变成1,1变成0,然后查询点(x,y)的值
*/
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>

using namespace std;

bool a[1005<<2][1005<<2];///二维线段树
int n;
void upy(int root,int rt,int l,int r,int y1,int y2)
{
    if(y1<=l&&r<=y2)
    {
        a[root][rt]=!a[root][rt];///找到合适的区间进行翻转
        return ;
    }
    int mid=(l+r)>>1;
    if(y1<=mid)upy(root,rt<<1,l,mid,y1,y2);///按y更新
    if(y2>mid) upy(root,rt<<1|1,mid+1,r,y1,y2);
}
void upx(int rt,int l,int r,int x1,int x2,int y1,int y2)
{
    if(x1<=l&&r<=x2)///先找出一维x
    {
        upy(rt,1,1,n,y1,y2);///在rt这个节点上在建y的线段树
        return ;
    }
    int mid=(l+r)>>1;
    if(x1<=mid)upx(rt<<1,l,mid,x1,x2,y1,y2);///按x更新
    if(x2>mid) upx(rt<<1|1,mid+1,r,x1,x2,y1,y2);
}
long long sum;

void qy(int root,int rt,int l,int r,int y)
{
    if(a[root][rt])sum++;///在x的root和y的rt这个区间上,
    ///如果是1就++,因为初始值为0,看翻转的次数
    if(l==r)return ;
    int mid=(l+r)>>1;///结束条件
    if(y<=mid)qy(root,rt<<1,l,mid,y);///根据y查找
    if(y>mid)qy(root,rt<<1|1,mid+1,r,y);
}
void qx(int rt,int l,int r,int x,int y)
{
    qy(rt,1,1,n,y);///在rt这个点上查询y
    if(l==r)return ;///递归结束条件
    int mid=(l+r)>>1;
    if(x<=mid)qx(rt<<1,l,mid,x,y);///查找x的位置
    if(x>mid)qx(rt<<1|1,mid+1,r,x,y);
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int T;
        char ch[5];
        int x,y,x1,x2,y1,y2;
        cin>>n>>T;
        memset(a,0,sizeof(a));
        while(T--)
        {
            scanf("%s",ch);
            if(ch[0]=='C')
            {
                scanf("%d%d%d%d",&x1,&y1,&x2,&y2);///输入更新的区间点
                upx(1,1,n,x1,x2,y1,y2);///更新线段树
            }
            else
            {
                scanf("%d%d",&x,&y);
                sum=0;
                qx(1,1,n,x,y);///在线段树中查询
                if(sum%2)printf("1\n");///看(x,y)的所有父节点一共翻转了几次
                else printf("0\n");
            }
        }
        printf("\n");
    }
    return 0;
}


/*
**name:二维线段树求最大值和最小值
**来源:HDU4819
*/
# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;

# define lson l,m,rt<<1
# define rson m+1,r,rt<<1|1
# define MAXN 805
int xL,xR,yL,yR,val;
int maxv,minv;
int Max[MAXN<<2][MAXN<<2],Min[MAXN<<2][MAXN<<2];
int N,mat[MAXN][MAXN];

void PushUp(int xrt,int rt)
{
    Max[xrt][rt]=max(Max[xrt][rt<<1],Max[xrt][rt<<1|1]);
    Min[xrt][rt]=min(Min[xrt][rt<<1],Min[xrt][rt<<1|1]);
}

void BuildY(int xrt,int x,int l,int r,int rt)
{
    int m;
    if(l==r)
    {
        if(x!=-1) Max[xrt][rt]=Min[xrt][rt]=mat[x][l];
        else
        {
            Max[xrt][rt]=max(Max[xrt<<1][rt],Max[xrt<<1|1][rt]);
            Min[xrt][rt]=min(Min[xrt<<1][rt],Min[xrt<<1|1][rt]);
        }
        return;
    }
    m=(l+r)>>1;
    BuildY(xrt,x,lson);
    BuildY(xrt,x,rson);
    PushUp(xrt,rt);
}

void BuildX(int l,int r,int rt)
{
    int m;
    if(l==r)
    {
        BuildY(rt,l,1,N,1);
        return;
    }
    m=(l+r)>>1;
    BuildX(lson);
    BuildX(rson);
    BuildY(rt,-1,1,N,1);
}

void UpdateY(int xrt,int x,int l,int r,int rt)
{
    int m;
    if(l==r)
    {
        if(x!=-1) Max[xrt][rt]=Min[xrt][rt]=val;
        else
        {
            Max[xrt][rt]=max(Max[xrt<<1][rt],Max[xrt<<1|1][rt]);
            Min[xrt][rt]=min(Min[xrt<<1][rt],Min[xrt<<1|1][rt]);
        }
        return;
    }
    m=(l+r)>>1;
    if(yL<=m) UpdateY(xrt,x,lson);
    else UpdateY(xrt,x,rson);
    PushUp(xrt,rt);
}

void UpdateX(int l,int r,int rt)
{
    int m;
    if(l==r)
    {
        UpdateY(rt,l,1,N,1);
        return;
    }
    m=(l+r)>>1;
    if(xL<=m) UpdateX(lson);
    else UpdateX(rson);
    UpdateY(rt,-1,1,N,1);
}


void QueryY(int xrt,int l,int r,int rt)
{
    int m;
    if(yL<=l&&yR>=r)
    {
        minv=min(minv,Min[xrt][rt]);
        maxv=max(maxv,Max[xrt][rt]);
        return; 
    }
    m=(l+r)>>1;
    if(yL<=m) QueryY(xrt,lson);
    if(yR>m) QueryY(xrt, rson);
}

void QueryX(int l,int r,int rt)
{
    int m;
    if(xL<=l&&xR>=r)
    {
        QueryY(rt,1,N,1);
        return; 
    }
    m=(l+r)>>1;
    if(xL<=m) QueryX(lson);
    if(xR>m) QueryX(rson);
}

int main()
{
    //freopen("in.txt","r",stdin);
    int i,j,q,cas,T,x,y,l;
    char op[5];
    scanf("%d",&T);
    for(cas=1;cas<=T;cas++)
    {
        scanf("%d",&N);
        for(i=1;i<=N;i++)
            for(j=1;j<=N;j++)
                scanf("%d",&mat[i][j]);
        BuildX(1,N,1);
        scanf("%d",&q);
        printf("Case #%d:\n",cas);
        while(q--)
        {
            scanf("%d%d%d",&x,&y,&l);
            l=(l+1)/2;
            xL=max(1,x-l+1),xR=min(N,x+l-1);
            yL=max(1,y-l+1),yR=min(N,y+l-1);
            minv=1<<30,maxv=-(1<<30);
            QueryX(1,N,1);
            val=(maxv+minv)/2;
            xL=x,yL=y;
            printf("%d\n",val);
            UpdateX(1,N,1);
        }
    }
    return 0;
}





/*
**name:二维线段树
**function:二维线段树求区间和
**题意:
一个矩阵,初始化为0,两种操作:
1、将某点增加val
2、查询一个子矩阵的和

**思路:
二维线段树,单点更新,区间求和,记得pushup.
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#define N 1050
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r

int sum[N*3][N*3];
int n;

void SubBuild(int rt,int l,int r,int t)
{
    sum[t][rt] = 0;
    if(l!=r)
    {
        int mid = (l + r) >> 1;
        SubBuild(lson,t);
        SubBuild(rson,t);
    }
}

void Build(int rt,int l,int r)
{
    SubBuild(1,1,n,rt);
    if(l!=r)
    {
        int mid = (l + r) >> 1;
        Build(lson);
        Build(rson);
    }
}

void SubUpdate(int rt,int l,int r,int y,int val,int t)
{
    if(l == r)
        sum[t][rt] += val;
    else
    {
        int mid = (l + r) >> 1;
        if(y <= mid) SubUpdate(lson,y,val,t);
        else         SubUpdate(rson,y,val,t);
        sum[t][rt] = sum[t][rt<<1] + sum[t][rt<<1|1];
    }
}

void Update(int rt,int l,int r,int x,int y,int val)
{
    SubUpdate(1,1,n,y,val,rt);
    if(l!=r)
    {
        int mid = (l + r) >> 1;
        if(x <= mid) Update(lson,x,y,val);
        else         Update(rson,x,y,val);
    }
}

__int64 SubQuery(int rt,int l,int r,int LY,int RY,int t)
{
    if(LY <= l && RY >= r)
        return sum[t][rt];
    int mid = (l + r) >> 1;
    __int64 ans = 0;
    if(LY <= mid) ans += SubQuery(lson,LY,RY,t);
    if(RY > mid ) ans += SubQuery(rson,LY,RY,t);
    return ans;
}

__int64 Query(int rt,int l,int r,int LX,int RX,int LY,int RY)
{
    if(LX <= l && RX >= r)
    {
        return SubQuery(1,1,n,LY,RY,rt);
    }
    int mid = (l + r) >> 1;
    __int64 ans = 0;
    if(LX <= mid) ans += Query(lson,LX,RX,LY,RY);
    if(RX > mid ) ans += Query(rson,LX,RX,LY,RY);
    return ans;
}

int main()
{
    int op;
    int a,b,c,d;
    while(scanf("%d %d",&op,&n)!=EOF)
    {
        ++n;
        Build(1,1,n);
        while(scanf("%d",&op))
        {
            if(op == 3)
                break;
            if(op == 1)
            {
                scanf("%d %d %d",&a,&b,&c);
                ++a,++b;
                Update(1,1,n,a,b,c);
            }
            else
            {
                scanf("%d %d %d %d",&a,&b,&c,&d);
                ++a,++b,++c,++d;
                printf("%I64d\n",Query(1,1,n,a,c,b,d));
            }
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值