zoukankan      html  css  js  c++  java
  • 线段树入门题

    先上板子(不过线段树的使用经常会有要修改的地方,需要自己能够独立的敲出来,这个和图论的那些板子就不一样了)

    单点更新,区间和查询

    /********************************
    线段树的单点更新和区间和查询 
    ********************************/
    #include <bits/stdc++.h>
    using namespace std;
    #define ls l,m,rt<<1
    #define rs m+1,r,rt<<1|1
    
    const int MAXN=50005;
    int tree[MAXN<<2];
    
    void push_up(int rt)
    {
        tree[rt]=tree[rt<<1]+tree[rt<<1|1];
    }
    
    void build(int l,int r,int rt)
    {     
        if(l==r)
        {
            scanf("%d",&tree[rt]);
            return ;
        } 
        int m=(l+r)>>1;
        build(ls); build(rs);
        push_up(rt);
    }
    
    void update(int x,int v,int l,int r,int rt)
    {    
        if(l==r)
        {
            tree[rt]=v;
            return ;
        }
        int m=(l+r)>>1;
        if(x<=m) update(x, v, ls);
        else      update(x, v, rs);
        push_up(rt);
    }
    
    int query(int L, int R, int l, int r, int rt)
    {   
        if(L<=l && r<=R) return tree[rt];
        int m=(l+r)>>1, ret=0;
        if(L<=m) ret+=query(L, R, ls);
        if(R> m) ret+=query(L, R, rs);
        return ret;
    }
    
    int main()
    {
        return 0;
    }
    View Code

    单点更新,区间最值查询

    /******************************* 
    线段树的单点更新和区间最值查询 
    *******************************/ 
    #include <bits/stdc++.h>
    using namespace std;
    #define ls l,m,rt<<1
    #define rs m+1,r,rt<<1|1
    
    const int MAXN=50005;
    int tree[MAXN<<2];
    
    void push_up(int rt)
    {
        tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);
    }
    
    void build(int l,int r,int rt)
    {    
        if(l==r)
        {
            scanf("%d",&tree[rt]);
            return ;
        } 
        int m=(l+r)>>1;
        build(ls); build(rs);
        push_up(rt);
    }
    
    void update(int x, int v, int l, int r, int rt)
    {    
        if(l==r)
        {
            tree[rt]=v;
            return ;
        }
        int m=(l+r)>>1;
        if(x<=m) update(x, v, ls);
        else      update(x, v, rs);
        push_up(rt);
    }
    
    int query(int L, int R, int l, int r, int rt)
    {   
        if(L<=l && r<=R) return tree[rt];
        int m=(l+r)>>1, ret=-1;
        if(L<=m) ret=max(ret, query(L, R, ls));
        if(R> m) ret=max(ret, query(L, R, rs));
        return ret;
    }
    
    int main()
    {
        return 0;
    }
    View Code

    区间更新,区间和查询

    注意:每次往下层query或者update的之前都需要延迟标记下放

    /***************************
    线段树的区间更新和区间和查询 
    ***************************/
    #include <bits/stdc++.h> 
    using namespace std;
    #define ll long long
    #define ls l,m,rt<<1
    #define rs m+1,r,rt<<1|1
    
    const int MAXN=50005; 
    ll tree[MAXN<<2], add[MAXN<<2];
    
    void push_up(int rt)
    {
        tree[rt]=tree[rt<<1]+tree[rt<<1|1];
    }
    
    void push_down(int rt, int m)//m是当前节点区间的范围的长度 
    {         
        if(add[rt])
        {
            add[rt<<1]+=add[rt]; 
            add[rt<<1|1]+=add[rt];
            tree[rt<<1]+=(m-(m>>1))*add[rt];
            tree[rt<<1|1]+=(m>>1)*add[rt];
            add[rt]=0;
        }
    }
    
    void build(int l, int r, int rt)
    {   
        add[rt]=0;
        if(l==r)
        {
            scanf("%lld",&tree[rt]);
            return ;
        }
        int m=(l+r)>>1;
        build(ls); build(rs);
        push_up(rt);
    }
    
    void update(int L, int R, int c, int l, int r, int rt)
    {    
        if(L<=l && r<=R) 
        {
            add[rt]+=c; 
            tree[rt]+=1ll*c*(r-l+1); 
            return ;
        }
        push_down(rt, r-l+1);
        int m=(l+r)>>1;
        if(L<=m) update(L, R, c, ls);
        if(R> m) update(L, R, c, rs);
        push_up(rt);
    }
    
    ll query(int L,int R,int l,int r,int rt)
    {  
        if(L<=l && r<=R) return tree[rt];
        push_down(rt, r-l+1);
        int m=(l+r)>>1;
        ll ret=0;
        if(L<=m) ret+=query(L, R, ls);
        if(R> m) ret+=query(L, R, rs);
        return ret;
    }
    
    int main()
    {
        return 0;
    }
    View Code

    POJ 3468(模板题,验板)

    /***************************
    线段树的区间更新和区间和查询 
    ***************************/
    //#include <bits/stdc++.h> 
    #include <cstdio>
    using namespace std;
    #define ll long long
    #define ls l,m,rt<<1
    #define rs m+1,r,rt<<1|1
    
    const int MAXN=1e5+5; 
    ll tree[MAXN<<2], add[MAXN<<2];
    
    void push_up(int rt)
    {
        tree[rt]=tree[rt<<1]+tree[rt<<1|1];
    }
    
    void push_down(int rt, int m)//m是当前节点区间的范围的长度 
    {         
        if(add[rt])
        {
            add[rt<<1]+=add[rt]; 
            add[rt<<1|1]+=add[rt];
            tree[rt<<1]+=(m-(m>>1))*add[rt];
            tree[rt<<1|1]+=(m>>1)*add[rt];
            add[rt]=0;
        }
    }
    
    void build(int l, int r, int rt)
    {   
        add[rt]=0;
        if(l==r)
        {
            scanf("%lld",&tree[rt]);
            return ;
        }
        int m=(l+r)>>1;
        build(ls); build(rs);
        push_up(rt);
    }
    
    void update(int L, int R, int c, int l, int r, int rt)
    {    
        if(L<=l && r<=R) 
        {
            add[rt]+=c; 
            tree[rt]+=1ll*c*(r-l+1); 
            return ;
        }
        push_down(rt, r-l+1);
        int m=(l+r)>>1;
        if(L<=m) update(L, R, c, ls);
        if(R> m) update(L, R, c, rs);
        push_up(rt);
    }
    
    ll query(int L, int R, int l, int r, int rt)
    {  
        if(L<=l && r<=R) return tree[rt];
        push_down(rt, r-l+1);
        int m=(l+r)>>1;
        ll ret=0;
        if(L<=m) ret+=query(L, R, ls);
        if(R> m) ret+=query(L, R, rs);
        return ret;
    }
    
    int main()
    {
        int n, q;
        scanf("%d%d", &n, &q);
        build(1, n, 1);
        while(q--)
        {
            char cmd[10];
            int a, b, c;
            scanf("%s%d%d", cmd, &a, &b); //scanf("%c")是会读取会回车的... 
            if(cmd[0]=='Q') printf("%lld
    ", query(a, b, 1, n, 1));
            else {
                scanf("%d", &c);
                update(a, b, c, 1, n, 1);
            }
        }
        return 0;
    }
    View Code

    HDU 1394(求逆序对的个数)

    注意:如果给出的数据不是离散且连续的话,需要先离散化。

    #include <bits/stdc++.h>
    using namespace std;
    #define _for(i,a,b) for(int i=(a); i< (b); i++)
    #define _rep(i,a,b) for(int i=(a); i<=(b); i++)
    #define ls l,m,rt<<1
    #define rs m+1,r,rt<<1|1
    
    const int MAXN=50005;
    int tree[MAXN<<2];
    
    void push_up(int rt)
    {
        tree[rt]=tree[rt<<1]+tree[rt<<1|1];
    }
    
    void build(int l,int r,int rt)
    {     
        if(l==r)
        {
            tree[rt]=0;
            return ;
        } 
        int m=(l+r)>>1;
        build(ls); build(rs);
        push_up(rt);
    }
    
    void update(int x,int v,int l,int r,int rt)
    {    
        if(l==r)
        {
            tree[rt]=v;
            return ;
        }
        int m=(l+r)>>1;
        if(x<=m) update(x, v, ls);
        else      update(x, v, rs);
        push_up(rt);
    }
    
    int query(int L, int R, int l, int r, int rt)
    {   
        if(L<=l && r<=R) return tree[rt];
        int m=(l+r)>>1, ret=0;
        if(L<=m) ret+=query(L, R, ls);
        if(R> m) ret+=query(L, R, rs);
        return ret;
    }
    int a[MAXN];
    
    int main()
    {
        ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
        int n;
        while(cin>>n)
        {
            int ans=0, sum=0;
            build(0, n-1, 1);
            _for(i, 0, n)
            {
                cin>>a[i];
                sum+=query(a[i]+1, n-1,  0, n-1, 1);
                update(a[i], 1, 0, n-1, 1);
            }
            ans=sum;
            _for(i, 0, n)
            {
                sum+=(n-1-a[i])-a[i];
                ans=min(ans, sum);
            }
            cout<<ans<<endl;    
        }
        return 0;
    }
    View Code

    HDU 2795

    题意:h*w的木板,放进一些1*L的物品(横着放)优先往上层放,求层数,不能放的话-1

    题解:线段树的底部对应h,值对应该层剩余的最大空间,向上更新最大值,每一次查询优先往左子树走,查询后进行更改,并向上更新。

       还有一点,这个h的范围很大,但是k的范围较小,h最多只要k层即可。

       备注:数据量很大输出很多,发现一个问题,cout时候<<endl 5100ms, <<" " 1600ms(比scanf还要快)

    #include <bits/stdc++.h>
    using namespace std;
    #define ls l,m,rt<<1
    #define rs m+1,r,rt<<1|1
    
    const int MAXN=2e5+5;
    int tree[MAXN<<2];
    
    int h, w, k;
    void push_up(int rt)
    {
        tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);
    }
    
    void build(int l,int r,int rt)
    {    
        if(l==r)
        {
            tree[rt]=w;
            return ;
        } 
        int m=(l+r)>>1;
        build(ls); build(rs);
        push_up(rt);
    }
    
    int query(int x, int l, int r, int rt)  //单点查询和更新 
    {
        if(l==r){
            tree[rt]-=x;
            return l;
        }
        int m=l+r>>1;
        int ret=(tree[rt<<1]>=x? query(x, ls):query(x, rs)); //优先往左子树走 
        push_up(rt);
        return ret; 
    }
    
    int main()
    {
        //freopen("in.txt", "r", stdin);
        ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
        while(cin>>h>>w>>k)
        {
            if(h>k) h=k;
            build(1, h, 1);
            while(k--)
            {
                int x; cin>>x;
                if(tree[1]<x) cout<<"-1"<<"
    ";
                else cout<<query(x, 1, h, 1)<<"
    ";
            }
        }
        return 0;
    }
    View Code

    POJ 2528 (离散化+线段树)

    题意:按次序给你贴广告时的位置(左端点和右端点),求能看得见多少个广告牌。

    题解:此题中l和r的范围很大,需要对点的坐标离散化(不改变数据的相对大小下(不影响结果),缩小数据的范围)。

       底层表示总区间,从后往前进行查询并覆盖,判断是否能看见。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define ls l,m,rt<<1
    #define rs m+1,r,rt<<1|1
    
    const int MAXN=2e4+5, MAXM=1e7+5;
    int tree[MAXN<<2], pos[MAXN], pl[MAXN], pr[MAXN];
    int hash[MAXM];
    void push_up(int rt)
    {
        tree[rt]=tree[rt<<1] && tree[rt<<1|1];
    }
    
    void build(int l,int r,int rt)
    {     
        tree[rt]=0;
        if(l==r) return ; 
        int m=(l+r)>>1;
        build(ls); build(rs);
    }
    
    int query(int L, int R, int l, int r, int rt)  
    {     //注意这个区间修改的过程中,因为这个只是0或者1,所以只用修改到完全包含该区间的位置即可(就是放延迟标记的地方即可)不用push_down 
        //往下查询的时候先判断是否被标记即可 (其实就是我们要查询的地方是否被延迟标记过了) 
        //同理,建树的时候,每一个节点都要归0(push_up加上去即可,或者直接赋值),都把我给调傻了....(其实感觉直接延迟标记成段更新反而没有这么多的细节Orz)  
        if(tree[rt]) return 0;
        if(L<=l && r<=R){
            tree[rt]=1;
            return 1;
        }
        int m=l+r>>1;
        /*
        int okl=1, okr=1;               WA
        if(L<=m)  okl=query(L, R, ls);
        if(R> m)  okr=query(L, R, rs);
        ok=okl||okr;
        */
        int ok, okl, okr;
        if(L>m) ok=query(L, R, rs);
        else if(R<=m) ok=query(L, R, ls);
        else 
        {
            okl=query(L, R, ls);
            okr=query(L, R, rs);
            ok=okl || okr;    
        } 
        push_up(rt);
        return ok;
    }
    
    void print(int l, int r, int rt) //debug
    {
        printf("! l=%d, r=%d, is_ok=%d
    ", l, r, tree[rt]);
        if(l==r) return; 
        int m=l+r>>1;
        print(ls); print(rs);    
    } 
    
    int main()
    {
        ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); //注意调试的时候用printf的时候不要解除流绑定 
    //    freopen("in.txt", "r", stdin);
        int T; 
        for(cin>>T; T--; )
        {
            int n; cin>>n; 
            for(int i=0; i<n; i++)
            {
                cin>>pl[i]>>pr[i];
                pos[2*i]=pl[i];
                pos[2*i+1]=pr[i];
            }
            sort(pos, pos+2*n);
            int cnt=unique(pos, pos+2*n)-pos;
            for(int i=0; i<cnt; i++)
                hash[pos[i]]=i;
            build(0, cnt-1, 1);
            int ans=0;
            for(int i=n-1; i>=0; i--)
            {
                if(query(hash[pl[i]], hash[pr[i]], 0, cnt-1, 1))
                {
                    //printf("! i=%d,l=%d, r=%d
    ", i, hash[pl[i]], hash[pr[i]]);
                    ans++;
                    //print(0, cnt-1, 1);
                    //printf("
    ");
                }
            }
            cout<<ans<<"
    ";
        }
        return 0;
    }
    View Code

    HDU 1542 矩形面积并(线段树+离散化+扫描线)

    题意:求多个矩形并后的总面积。

    题解:扫描线,从下往上扫,cnt记录每一个区间的下位边和上位边的差值,sum记录每个区间下该条扫描线的有效覆盖长度。

       用线段树维护这两个值,push_up分几种情况,别漏了。

       数据是浮点数,离散化线段树的l,r和对应每一段区间的编号即可。

       备注:这题调了好久,1,sum在计算的时候出现了负数,发现ls和rs预定义的时候<<写反了(太sb了)。 2.用lower_bound查询会MLE(等我去查查)。

    #include <bits/stdc++.h>
    using namespace std;
    #define ls l, m, rt<<1
    #define rs m+1, r, rt<<1|1
    
    const int MAXN=2e3+5;
    double sum[MAXN<<2], X[MAXN];
    int cnt[MAXN<<2];
    
    struct Line{
        double l, r, h;
        int f; 
        Line() {}
        Line(double a, double b, double c, int d) : l(a), r(b), h(c), f(d) {}
        bool operator <(const Line &b) const {
            return h<b.h;
        }
    }lines[MAXN];
    
    void push_up(int rt, int l, int r)
    {
        if(cnt[rt]) sum[rt]=X[r+1]-X[l];  //该区间被完全覆盖,注意cnt的范围一定是>=0的 
        else if(l==r) sum[rt]=0;          //该区间是叶子节点,且cnt==0 
        else sum[rt]=sum[rt<<1]+sum[rt<<1|1]; //该区间不是叶子节点,且没有被完全覆盖 
    }
    
    void update(int L, int R, int c, int l, int r, int rt)
    {
        if(L<=l && r<=R){
            cnt[rt]+=c;
            push_up(rt, l, r);
            return ;
        }
        int m=l+r>>1;
        if(L<=m) update(L, R, c, ls);
        if(R> m) update(L, R, c, rs);
        push_up(rt, l, r);
    }
    
    int bin(double val, int l, int r, double x[])
    {
        while(l<=r)
        {
            int m=l+r>>1;
            if(x[m]==val) return m;
            else if(x[m]<val) l=m+1;
            else if(x[m]>val) r=m-1; 
        }
        return -1;
    }
    
    int main()
    {
        //freopen("in.txt", "r", stdin);
        int n, kase=0; 
        while(cin>>n, n)
        {
            int m=0;
            for(int i=0; i<n; i++)
            {
                double x1, y1, x2, y2;
                cin>>x1>>y1>>x2>>y2;
                X[m]=x1; lines[m++]=Line(x1, x2, y1, 1);
                X[m]=x2; lines[m++]=Line(x1, x2, y2, -1);
            }
            sort(X, X+m);
            sort(lines, lines+m);
            int k=unique(X, X+m)-X;
            memset(sum, 0, sizeof(sum));
            memset(cnt, 0, sizeof(cnt)); //build
            double ans=0; 
            for(int i=0; i<m-1; i++)
            {
                //int l=lower_bound(X, X+m, lines[i].l)-X;
                //int r=lower_bound(X, X+m, lines[i].r)-X-1;  MLE 不明白为啥会这样 
                int l=bin(lines[i].l, 0, k-1, X);
                int r=bin(lines[i].r, 0, k-1, X)-1;
                if(l<=r) update(l, r, lines[i].f, 0, k-1, 1);
                ans+=sum[1]*(lines[i+1].h-lines[i].h);
            }
            printf("Test case #%d
    Total explored area: %.2lf
    
    ",++kase, ans);
        }
        return 0;
    } 
    View Code

    POJ 3667 区间合并

    题意: 2个指令1 a:询问是不是有连续长度为a的空房间,输出最左边起始的位置,并占领该段区间  2 a b:将[a,a+b-1]的房间清空

    题解:区间合并入门题。线段树维护左右子树,左边,中间,右边,和交叉的空闲区间的长度。

    //#include <bits/stdc++.h>
    #include <iostream>
    #include <cstdio>
    using namespace std;
    #define ls l, m, rt<<1
    #define rs m+1, r, rt<<1|1 
    
    const int MAXN=5e4+5;
    int cover[MAXN<<2]; 
    int lsum[MAXN<<2], rsum[MAXN<<2], msum[MAXN<<2];
    
    void push_up(int rt, int m)
    {
        lsum[rt]=lsum[rt<<1]; rsum[rt]=rsum[rt<<1|1];
        if(lsum[rt]==m-m/2) lsum[rt]+=lsum[rt<<1|1]; //注意这里别写成了m-m>>1 
        if(rsum[rt]==m/2  ) rsum[rt]+=rsum[rt<<1];
        msum[rt]=max(rsum[rt<<1]+lsum[rt<<1|1], max(msum[rt<<1], msum[rt<<1|1]));  //分3种情况 
    }
    
    void push_down(int rt, int m)
    {
        if(cover[rt]!=-1)
        {
            cover[rt<<1]=cover[rt<<1|1]=cover[rt];
            lsum[rt<<1]=rsum[rt<<1]=msum[rt<<1]= cover[rt]? 0:m-m/2;
            lsum[rt<<1|1]=rsum[rt<<1|1]=msum[rt<<1|1]= cover[rt]? 0:m/2;
            cover[rt]=-1;
        } 
    }
    
    void build(int l, int r, int rt)
    {
        lsum[rt]=rsum[rt]=msum[rt]=r-l+1;
        cover[rt]=-1;    //归为-1,更新的时候会放0,或者1的延迟标记  
        if(l==r) return ;
        int m=l+r>>1;
        build(ls); build(rs);
    }
    
    int query(int a, int l, int r, int rt)
    {
        if(l==r) return l;
        push_down(rt, r-l+1);
        int m=l+r>>1;
        if(msum[rt<<1]>=a) return query(a, ls); //优先向左走,三种情况,左中右 
        else if(rsum[rt<<1]+lsum[rt<<1|1]>=a) return m-rsum[rt<<1]+1;
        else return query(a, rs);
    }
    
    void update(int L, int R, int v, int l, int r, int rt)
    {
        if(L<=l && r<=R) 
        {
            lsum[rt]=rsum[rt]=msum[rt]= v? 0:r-l+1;
            cover[rt]=v;
            return ;
        }
        push_down(rt, r-l+1);
        int m=l+r>>1;
        if(L<=m) update(L, R, v, ls);
        if(R >m) update(L, R, v, rs);
        push_up(rt, r-l+1);
    }
    
    void print(int l, int r, int rt)
    {
        printf("! l=%d, r=%d, lsum=%d, rsum=%d, msum=%d
    ", l, r, lsum[rt], rsum[rt], msum[rt]);
        if(l==r) return ;
        int m=l+r>>1;
        print(ls); print(rs);
    }
    
    int main()
    {
        ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
        //freopen("in.txt", "r", stdin);
        int n, m;
        cin>>n>>m;
        build(1, n, 1);
        while(m--)
        {
            int cmd, a, b;
            cin>>cmd;
            if(cmd==1){
                cin>>a; 
                if(msum[1]<a){
                    cout<<"0
    "; continue;
                }
                int pos=query(a, 1, n, 1);
                cout<<pos<<"
    ";
                update(pos, pos+a-1, 1, 1, n, 1);
            } 
            else{
                cin>>a>>b;
                update(a, a+b-1, 0, 1, n, 1);
            }
    //        printf("
    ");
    //        print(1, n, 1);
    //        printf("
    ");
        }    
        return 0;
    } 
    View Code
  • 相关阅读:
    render :template 和 render :parital
    routes.rb和link_to的一些规则
    rails程序文件名命名规范
    Ruby的模型关系随笔
    Ruby的require相关知识
    rails中的form_for
    rails中的session
    RubyMine不能调试Rails项目的问题
    序列化后的json串与解析json串实体间的关系
    mysql还原数据库
  • 原文地址:https://www.cnblogs.com/Yokel062/p/11352683.html
Copyright © 2011-2022 走看看