Vision_数据结构_线段树

来源:互联网 发布:科尔沃折叠刀淘宝 编辑:程序博客网 时间:2024/06/10 07:39
///定义:
/*
    线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,
每个单元区间对应线段树中的一个叶结点。使用线段树可以快速的查找某一个节点在
若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,实
际应用时一般还要开4N的数组以免越界,因此有时需要离散化让空间压缩。
*/

///代码:

/***name:一维线段树**function:一维线段树的最值、区间和,区间更新*/#include <bits/stdc++.h>#define MAXN 100010#define inf 0x3f3f3f3fusing 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 805int 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、将某点增加val2、查询一个子矩阵的和**思路:二维线段树,单点更新,区间求和,记得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,rint 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;}


原创粉丝点击