zoukankan      html  css  js  c++  java
  • 线段树经典类型归纳

    第一道:

    HDU 1754 单点更新。区间查询最大值,水题……

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include<set>
    #include<cmath>
    #include<bitset>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define lson i<<1,l,mid
    #define rson i<<1|1,mid+1,r
    #define INF 1000000
    #define maxn 1000010
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    int Max[maxn],a[200005],n,m,i;
    void pushup(int i)
    {
        Max[i]=max(Max[i<<1],Max[i<<1|1]);
    }
    void build(int i,int l,int r)
    {
        if(l==r)
        {
            Max[i]=a[l];
            return ;
        }
        int mid=(l+r)>>1;
        build(lson);build(rson);
        pushup(i);
    }
    void update(int i,int l,int r,int x,int v)
    {
        if(l==r&&l==x)
        {
            Max[i]=v;
            return ;
        }
        int mid=(l+r)>>1;
        if(x<=mid) update(lson,x,v);
        else update(rson,x,v);
        pushup(i);
    }
    int query(int i,int l,int r,int L,int R)
    {
        if(L<=l&&r<=R) return Max[i];
        int mid=(l+r)>>1,ans=0;
        if(L<=mid) ans=max(ans,query(lson,L,R));
        if(R>mid) ans=max(ans,query(rson,L,R));
        return ans;
    }
    int main()
    {
        while(~scanf("%d%d",&n,&m))
        {
            for(i=1;i<=n;i++)
                scanf("%d",a+i);
            build(1,1,n);
            int x,y;
            char str[3];
            while(m--)
            {
                scanf("%s%d%d",str,&x,&y);
                if(str[0]=='Q')
                    printf("%d
    ",query(1,1,n,x,y));
                else update(1,1,n,x,y);
            }
        }
        return 0;
    }
    

    第二道:

    HDU 1698 区间更新,区间查询,lazy标记种类,水……

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include<set>
    #include<cmath>
    #include<bitset>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define lson i<<1,l,mid
    #define rson i<<1|1,mid+1,r
    #define INF 1000000
    #define maxn 400010
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    int sum[maxn],lazy[maxn],n,q;
    void pushup(int i)
    {
        sum[i]=sum[i<<1]+sum[i<<1|1];
    }
    void pushdown(int i,int l,int r)
    {
        if(lazy[i])
        {
            int mid=(l+r)>>1;
            lazy[i<<1]=lazy[i<<1|1]=lazy[i];
            sum[i<<1]=(mid-l+1)*lazy[i<<1];
            sum[i<<1|1]=(r-mid)*lazy[i<<1|1];
            lazy[i]=0;
        }
    }
    void update(int i,int l,int r,int L,int R,int v)
    {
        if(l==L&&r==R)
        {
            sum[i]=(r-l+1)*v;
            lazy[i]=v;
            return ;
        }
        int mid=(l+r)>>1;
        pushdown(i,l,r);
        if(R<=mid) update(lson,L,R,v);
        else if(L>mid) update(rson,L,R,v);
        else
        {
            update(lson,L,mid,v);
            update(rson,mid+1,R,v);
        }
        pushup(i);
    }
    void build(int i,int l,int r)
    {
        if(l==r)
        {
            sum[i]=1;
            lazy[i]=1;
            return ;
        }
        sum[i]=lazy[i]=0;
        int mid=(l+r)>>1;
        build(lson);build(rson);
        pushup(i);
    }
    int main()
    {
        int t;
        cin>>t;
        for(int i=1;i<=t;i++)
        {
            scanf("%d%d",&n,&q);
            build(1,1,n);
            int l,r,v;
            while(q--)
            {
                scanf("%d%d%d",&l,&r,&v);
                update(1,1,n,l,r,v);
            }
            printf("Case %d: The total value of the hook is %d.
    ",i,sum[1]);
        }
        return 0;
    }
    

    第三道:

    HDU 1394 单点更新。区间查询,线段树、树状数组、归并排序求逆序数。

    解法一:线段树

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include<set>
    #include<cmath>
    #include<bitset>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define lson i<<1,l,mid
    #define rson i<<1|1,mid+1,r
    #define INF 1000000007
    #define maxn 400010
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    int sum[20005],a[5005];
    void update(int i,int l,int r,int x,int v)
    {
        sum[i]+=v;
        if(l==r) return ;
        int mid=(l+r)>>1;
        if(x<=mid) update(lson,x,v);
        else update(rson,x,v);
    }
    int query(int i,int l,int r,int L,int R)
    {
        if(l==L&&r==R) return sum[i];
        int mid=(l+r)>>1;
        if(R<=mid) return query(lson,L,R);
        else if(L>mid) return query(rson,L,R);
        else return query(lson,L,mid)+query(rson,mid+1,R);
    }
    int main()
    {
        int n,i;
        while(~scanf("%d",&n))
        {
            int ans=0,Min=INF;
            mem(sum,0);
            for(i=1;i<=n;i++)
            {
                scanf("%d",a+i),a[i]++;
                ans+=query(1,1,n,a[i],n);
                update(1,1,n,a[i],1);
            }
            Min=min(Min,ans);
            for(i=1;i<=n;i++)
            {
                ans-=a[i]-1;
                ans+=n-a[i];
                Min=min(Min,ans);
            }
            printf("%d
    ",Min);
        }
        return 0;
    }
    

    解法二:树状数组:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include<set>
    #include<cmath>
    #include<bitset>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define lson i<<1,l,mid
    #define rson i<<1|1,mid+1,r
    #define INF 1000000007
    #define maxn 400010
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    int n,c[maxn],a[maxn];
    void update(int x)
    {
        for(int i=x;i;c[i]+=1,i-=i&(-i));
        //更新x之前的,这样查询x+1之后的数才会查询不到逆序数
    }
    int query(int x)
    {
        int i,sum=0;
        for(i=x;i<=n;sum+=c[i],i+=i&(-i));
        return sum;
    }
    int main ()
    {
        while(~scanf("%d",&n))
        {
            int i,sum=0,Min=INF;
            mem(c,0);
            for(i=1;i<=n;i++)
            {
                scanf("%d",a+i),a[i]++;
                sum+=query(a[i]);
                update(a[i]);
            }
            Min=min(sum,Min);
            for(i=1;i<=n;i++)
            {
                sum-=a[i]-1;
                sum+=n-a[i];
                Min=min(sum,Min);
            }
            printf("%d
    ",Min);
        }
        return 0;
    }
    

    第四道:

    POJ 2528 线段树+离散化

    这题T了几个小时了。晕死了……原来是在离散化的时候多了点时间。尼玛,让我debug了两个小时!!!。

    事实上我这样离散化还有个bug,由于单点离散化和区间离散化是不一样的。

    比方这个样例:

    3

    1 10

    1 3

    6 10

    我这个代码就输出2。本来是3的。POJ 的数据弱了。

    只是改一点就能够过了,离散化的时候能够离散成:Map[c[i]]=(i+1)*2,然后查询的时候,总区间也*2即可了。这样在区间与区间之间就能够查询得到了。

    懒得改了。想做好的就按这样做吧。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include<set>
    #include<cmath>
    #include<bitset>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define lson i<<1,l,mid
    #define rson i<<1|1,mid+1,r
    #define INF 10000010
    #define maxn 80010
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    int lazy[maxn];
    int a[10005],b[10005],c[20010],num[20010];
    set<int>s;
    void pushdown(int i,int v)
    {
        if(lazy[i]&&lazy[i]!=v)
        {
            lazy[i<<1]=lazy[i<<1|1]=lazy[i];
            lazy[i]=0;
        }
    }
    void update(int i,int l,int r,int L,int R,int v)
    {
        if(l==L&&r==R)
        {
            lazy[i]=v;
            return ;
        }
        int mid=(l+r)>>1;
        pushdown(i,v);
        if(R<=mid) update(lson,L,R,v);
        else if(L>mid) update(rson,L,R,v);
        else
        {
            update(lson,L,mid,v);
            update(rson,mid+1,R,v);
        }
    }
    void query(int i,int l,int r)
    {
        if(lazy[i])
        {
            s.insert(lazy[i]);
            return ;
        }
        if(l==r||lazy[i]==-1) return ;
        int mid=(l+r)>>1;
        query(lson);query(rson);
    }
    map<int,int>Map;
    int main()
    {
        int t;
        scanf("%d",&t);
        while(t--)
        {
            int i,n,k=0;
            Map.clear();
            s.clear();
            mem(lazy,0);
            scanf("%d",&n);
            for(i=0;i<n;i++)
            {
                scanf("%d%d",&a[i],&b[i]);
                c[k++]=a[i];
                c[k++]=b[i];
            }
            sort(c,c+k);
            k=unique(c,c+k)-c;
            for(i=0;i<k;i++)
                Map[c[i]]=i+1;
            for(i=0;i<n;i++)
                update(1,1,k,Map[a[i]],Map[b[i]],i+1);
            query(1,1,k);
            printf("%d
    ",s.size());
        }
        return 0;
    }


    第五道:

    HDU 2795 几何中的单点更新

    思路:刚開始看这题的时候也不知道该怎么用线段树做,想了好久也不知道,然后看了下别人的解题报告才知道这么easy,仅仅是想不到啊!用叶结点表示长方形的行。然后叶结点的域表示的是当前行的宽(或者还剩多少能够容纳的宽度),这样就能够轻松攻克了。线段树不建太长,有多少行就建多少,假设n大的话,就建n的长度即可了。这点看刚才看题的时候10^9就怕了。所以就没想到这么机智的做法。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include<set>
    #include<cmath>
    #include<bitset>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define lson i<<1,l,mid
    #define rson i<<1|1,mid+1,r
    #define INF 10000010
    #define maxn 800010
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    int Max[maxn];
    void pushup(int i)
    {
        Max[i]=max(Max[i<<1],Max[i<<1|1]);
    }
    void update(int i,int l,int r,int v)
    {
        if(l==r)
        {
            printf("%d
    ",l);
            Max[i]-=v;
            return ;
        }
        int mid=(l+r)>>1;
        if(Max[i<<1]>=v) update(lson,v);
        else update(rson,v);//用域比較v
        pushup(i);
    }
    int main()
    {
        int h,w,n;
        while(~scanf("%d%d%d",&h,&w,&n))
        {
            int i,len=h<n?h:n;
            for(int i=0;i<len*4+10;i++)
                Max[i]=w;
            while(n--)
            {
                int v;
                scanf("%d",&v);
                if(Max[1]<v) puts("-1");
                else update(1,1,len,v);
            }
        }
        return 0;
    }
    

    第六道:

    POJ 3667  区间合并,设置左右中区间,然后更新的时候合并。刚開始想的时候确实没想到这么机智的做法,我想的做法是加个lazy标记,然后查询的时候再加个flag标记,初始flag=0。假设找到flag=1,然后假设=1的话,递归就不进行下去了, 这样应该也能够做这道题,只是可能代码比較长忽悠一点吧……

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include<set>
    #include<cmath>
    #include<bitset>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define lson i<<1,l,mid
    #define rson i<<1|1,mid+1,r
    #define INF 10000010
    #define maxn 200010
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    int sum[maxn],lsum[maxn],rsum[maxn],lazy[maxn];
    void pushdown(int i,int v)
    {
        if(lazy[i]!=-1)
        {
            int l=i<<1,r=i<<1|1;
            lazy[l]=lazy[r]=lazy[i];
            sum[l]=lsum[l]=rsum[l]=(lazy[i]?0:(v-(v>>1)));//lazy为1表清空房间
            sum[r]=lsum[r]=rsum[r]=(lazy[i]?0:(v>>1));//右孩子值
            lazy[i]=-1;
        }
    }
    void pushup(int i,int v)
    {
        int l=i<<1,r=i<<1|1;
        lsum[i]=lsum[l];
        if(lsum[l]==v-(v>>1))//若左子树全空
            lsum[i]+=lsum[r];//则加上右子树的左区间
        rsum[i]=rsum[r];
        if(rsum[r]==v>>1)
            rsum[i]+=rsum[l];
        sum[i]=max(max(sum[l],sum[r]),rsum[l]+lsum[r]);
    }
    void update(int i,int l,int r,int L,int R,int v)
    {
        if(L<=l&&r<=R)
        {
            sum[i]=lsum[i]=rsum[i]=(v?

    0:r-l+1); lazy[i]=v; return ; } pushdown(i,r-l+1); int mid=(l+r)>>1; if(L<=mid) update(lson,L,R,v); if(mid<R) update(rson,L,R,v); pushup(i,r-l+1); } int query(int i,int l,int r,int v) { if(l==r) return l; pushdown(i,r-l+1); int mid=(l+r)>>1; if(sum[i<<1]>=v) return query(lson,v); else if(rsum[i<<1]+lsum[i<<1|1]>=v) return mid-rsum[i<<1]+1; return query(rson,v); } void build(int i,int l,int r) { sum[i]=lsum[i]=rsum[i]=r-l+1; lazy[i]=-1; if(l==r) return ; int mid=(l+r)>>1; build(lson);build(rson); } int main() { //freopen("test.txt","r",stdin); int n,m; scanf("%d%d",&n,&m); build(1,1,n); while(m--) { int q,l,r; scanf("%d%d",&q,&l); if(q==1) { if(sum[1]<l) {puts("0");continue;} int k=query(1,1,n,l); printf("%d ",k); update(1,1,n,k,k+l-1,1);//住房 } else { scanf("%d",&r); update(1,1,n,l,l+r-1,0); } } return 0; }


    第七道:

    POJ 2892  & HDU 1540

    解法一:树状数组查找第k小的数的下标。

    树状数组查找第k小的数做的,挺机智的。

    查找第k小的数,这个知识学习了一下午才知道什么意思。尼玛……事实上意思就是查找值为k的数在树状数组中其和从1開始到哪个下标其和等于k。比方树状数组中的:c[1]=1,c[2]=1,c[3]=0,c[4]=1,c[5]=0,c[6]=0,c[7]=1,c[8]=2,c[9]=1.

    则sum[5]=1,如今查找k=1的下标,那就是4;再比方sum[8]=2,查找k=2的下标就是8。这个求法能够用以下代码的find_kth函数二分逼近法求出。

    可是在HDU 1540上过不了,所说是数据水了。

    改了一上午还是过不了。还以为是数据有错了,然后拿别人代码交了一发线段树就过了,难道这题限制用树状数组?算了。还是用线段树写发区间合并吧!

    
    

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include<set>
    #include<stack>
    #include<cmath>
    #include<bitset>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define lowbit(x) (x&-x)
    #define INF 1000000
    #define maxn 100010
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    int c[50010];
    void update(int x,int n,int val)
    {
        for(; x<=n; x+=lowbit(x))
            c[x]+=val;
    }
    int sum(int x,int n)
    {
        int ans=0;
        for(; x>0; x-=lowbit(x))
            ans+=c[x];
        return ans;
    }
    int find_kth(int k,int n,int logn)
    {
        int cur=0,cnt=0,i;
        for(i=logn; i>=0; i--)
        {
            cur+=1<<i;
            if(cur>n||cnt+c[cur]>=k) cur-=1<<i;
            else cnt+=c[cur];
        }
        return cur+1;
    }
    int main(void)
    {
        int n,m,x;
        char s[2];
        scanf("%d%d",&n,&m);
        stack<int>st;
        n+=2;
        int logn=log(n+1.0)/log(2.0);
        update(1,n,1);
        update(n,n,1);
        while(m--)
        {
            scanf("%s",s);
            if(s[0]=='D')
            {
                scanf("%d",&x);
                x++;
                update(x,n,1);
                st.push(x);
            }
            else if(s[0]=='R')
            {
                x=st.top();
                st.pop();
                update(x,n,-1);
            }
            else
            {
                scanf("%d",&x);
                x++;
                int k=sum(x,n);
                int pre=find_kth(k,n,logn);
                int next=find_kth(k+1,n,logn);
                if(pre==x) puts("0");
                else printf("%d
    ",next-pre-1);
            }
        }
        return 0;
    }
    

    解法二:线段树区间合并

    这个和hotel那题差点儿相同,都是区间合并,代码差点儿相同。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include<set>
    #include<stack>
    #include<cmath>
    #include<bitset>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define lson i<<1,l,mid
    #define rson i<<1|1,mid+1,r
    #define INF 10000010
    #define maxn 200010
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    int sum[maxn],lsum[maxn],rsum[maxn];
    void pushup(int i,int v)
    {
        int l=i<<1,r=i<<1|1;
        lsum[i]=lsum[l];
        if(lsum[l]==v-(v>>1))//若左子树全空
            lsum[i]+=lsum[r];//则加上右子树的左区间
        rsum[i]=rsum[r];
        if(rsum[r]==v>>1)
            rsum[i]+=rsum[l];
        sum[i]=max(max(sum[l],sum[r]),rsum[l]+lsum[r]);
    }
    void update(int i,int l,int r,int x,int v)
    {
        if(l==r)
        {
            sum[i]=lsum[i]=rsum[i]=v;
            return ;
        }
        int mid=(l+r)>>1;
        if(x<=mid) update(lson,x,v);
        else update(rson,x,v);
        pushup(i,r-l+1);
    }
    int query(int i,int l,int r,int v)
    {
        if(l==r||sum[i]==0||sum[i]==r-l+1) return sum[i];
        int mid=(l+r)>>1;
        if(v<=mid)
        {
            if(v>=mid-rsum[i<<1]+1)
                return query(lson,v)+query(rson,mid+1);
            else return query(lson,v);
        }
        else
        {
            if(v<=mid+lsum[i<<1|1])
                return query(rson,v)+query(lson,mid);
            else return query(rson,v);
        }
    }
    void build(int i,int l,int r)
    {
        sum[i]=lsum[i]=rsum[i]=r-l+1;
        if(l==r) return ;
        int mid=(l+r)>>1;
        build(lson);
        build(rson);
    }
    int main()
    {
        //freopen("test.txt","r",stdin);
        int n,m;
        while(~scanf("%d%d",&n,&m))
        {
            build(1,1,n);
            stack<int>st;
            while(m--)
            {
                int x;
                char s[2];
                scanf("%s",s);
                if(s[0]=='D')
                {
                    scanf("%d",&x);
                    st.push(x);
                    update(1,1,n,x,0);
                }
                else if(s[0]=='R')
                {
                    x=st.top();st.pop();
                    update(1,1,n,x,1);
                }
                else
                {
                    scanf("%d",&x);
                    printf("%d
    ",query(1,1,n,x));
                }
            }
        }
        return 0;
    }
    

    第八道:

    HDU 2871  区间合并 

    这题比較晕……靠……刚開始直接输入scanf("%s%d",s,%l);然后一直T,检查了好久都没发现哪里有超时的可能。然后不用文件输入,手动输入的时候才发现Reset后面不用输数字了。尼玛……晕……

    与hotel那题的函数是一样的,仅仅只是处理不一样。

    只是这里由于是一堆一堆处理的,所以比較麻烦,可是线段树的函数还是不变的。

    由于要处理询问一段一段,所以能够把其放进向量vector里记录,增加和删除比較方便。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #define lson i<<1,l,mid
    #define rson i<<1|1,mid+1,r
    #define maxn 200010
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    int sum[maxn],lsum[maxn],rsum[maxn],lazy[maxn];
    void pushdown(int i,int v)
    {
        if(lazy[i]!=-1)
        {
            int l=i<<1,r=i<<1|1;
            lazy[l]=lazy[r]=lazy[i];
            sum[l]=lsum[l]=rsum[l]=(lazy[i]?0:(v-(v>>1)));//lazy为1表清空房间
            sum[r]=lsum[r]=rsum[r]=(lazy[i]?

    0:(v>>1));//右孩子值 lazy[i]=-1; } } void pushup(int i,int v) { int l=i<<1,r=i<<1|1; lsum[i]=lsum[l]; if(lsum[l]==v-(v>>1))//若左子树全空 lsum[i]+=lsum[r];//则加上右子树的左区间 rsum[i]=rsum[r]; if(rsum[r]==v>>1) rsum[i]+=rsum[l]; sum[i]=max(max(sum[l],sum[r]),rsum[l]+lsum[r]); } void update(int i,int l,int r,int L,int R,int v) { if(L<=l&&r<=R) { sum[i]=lsum[i]=rsum[i]=(v?0:r-l+1); lazy[i]=v; return ; } pushdown(i,r-l+1); int mid=(l+r)>>1; if(L<=mid) update(lson,L,R,v); if(mid<R) update(rson,L,R,v); pushup(i,r-l+1); } int query(int i,int l,int r,int v) { if(sum[i]<v) return 0; if(l==r) return l; pushdown(i,r-l+1); int mid=(l+r)>>1; if(sum[i<<1]>=v) return query(lson,v); else if(rsum[i<<1]+lsum[i<<1|1]>=v) return mid-rsum[i<<1]+1; return query(rson,v); } void build(int i,int l,int r) { sum[i]=lsum[i]=rsum[i]=r-l+1; lazy[i]=-1; if(l==r) return ; int mid=(l+r)>>1; build(lson); build(rson); } struct block { int Begin,End; }y; bool cmp(const block &a,const block &b) { return a.Begin<b.Begin; } vector<block>v; vector<block>::iterator it; int main() { //freopen("test.txt","r",stdin); int n,m; while(~scanf("%d%d",&n,&m)) { build(1,1,n); v.clear(); while(m--) { int l; char s[7]; scanf("%s",&s); if(s[0]=='N') { scanf("%d",&l); if(sum[1]<l) { puts("Reject New");continue; } int k=query(1,1,n,l); printf("New at %d ",k); update(1,1,n,k,k+l-1,1); y.Begin=k,y.End=k+l-1; it=upper_bound(v.begin(),v.end(),y,cmp); v.insert(it,y); } else if(s[0]=='F') { scanf("%d",&l); y.Begin=l,y.End=l; it=upper_bound(v.begin(),v.end(),y,cmp); int ii=it-v.begin()-1; if(ii==-1||v[ii].End<l) puts("Reject Free"); else { printf("Free from %d to %d ",v[ii].Begin,v[ii].End); update(1,1,n,v[ii].Begin,v[ii].End,0); v.erase(v.begin()+ii); } } else if(s[0]=='R') { //build(1,1,n);会超时 update(1,1,n,1,n,0); v.clear(); puts("Reset Now"); } else { scanf("%d",&l); if(l>v.size()) puts("Reject Get"); else printf("Get at %d ",v[l-1].Begin); } } puts(""); } return 0; }


    第九道:

    HDU 1542 開始利用线段树求解矩形面积的并、交、以及周长.

    刚開始接触这类型的时候。真的不知道该怎样下手。

    没想到线段树还能拿来求解矩形面积的并、交和周长,感觉挺神的。后面就看了这方面的博客,看到一个挺好的解释和代码,研究了一天半才所有搞明确……

    先看这个博客前面线段树中结点域的解释。然后边看图边看代码就理解了。

    昨天看明确之后,还以为所有理解了,然后今天敲的时候。没得答案,然后把过程都输出了。一个一个对照。然后才发现建树的时候错了。

    我们一般建树都是build(i<<1,l,mid);build(1<<1|1,mid+1,r);而这个线段树求解矩形的并不这样建的。有点差别。

    由于这样建树的话,叶子结点l和r同样,就是一个点,而不是线段了。所以要让叶子结点是线段,然后又不能在更新线段的时候变成更新一个点,所以得这样建树:build(1<<1,l,mid);build(1<<1|1,mid,r);建的这个右子树不一样。

    学习參考博客:http://www.cnblogs.com/ka200812/archive/2011/11/13/2247064.html

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include<set>
    #include<cmath>
    #include<bitset>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define lson i<<1,l,mid
    #define rson i<<1|1,mid+1,r
    #define INF 510010
    #define maxn 400010
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    double y[210];
    struct line
    {
        double x,y1,y2;
        int flag;
    }a[210];
    bool cmp(line a,line b)
    {
        return a.x<b.x;
    }
    double len[1000];
    int lazy[1000];
    void pushUp(int i,int l,int r)
    {
        if(lazy[i]) len[i]=y[r-1]-y[l-1];
        else if(l+1==r) len[i]=0;//叶子结点
        else len[i]=len[i<<1]+len[i<<1|1];
    }
    void update(int i,int l,int r,int L,int R,int c)
    {
        if(r<=L||l>=R) return ;
        if(L<=l&&r<=R)
        {
            lazy[i]+=c;
            pushUp(i,l,r);
            return;
        }
        int mid=(l+r)>>1;
        update(lson,L,R,c);
        update(i<<1|1,mid,r,L,R,c);//建的树不一样
        pushUp(i,l,r);
    }
    int main()
    {
        //freopen("test.txt","r",stdin);
        int n,ii=1;
        while(scanf("%d",&n)&&n)
        {
            int i;
            double x1,y1,x2,y2,ans=0;
            mem(len,0);mem(lazy,0);
            for(i=0;i<n;i++)
            {
                scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
                y[i<<1]=y1,a[i<<1].x=x1,a[i<<1].y1=y1,a[i<<1].y2=y2,a[i<<1].flag=1;
                y[i<<1|1]=y2,a[i<<1|1].x=x2,a[i<<1|1].y1=y1,a[i<<1|1].y2=y2,a[i<<1|1].flag=-1;
            }
            sort(y,y+n*2); //由于是浮点数,并且数又大。不好建树
            int nn=unique(y,y+n*2)-y;//所以离散化了再建树
            sort(a,a+n*2,cmp);
            printf("Test case #%d
    ",ii++);
            for(i=0;i<n*2-1;i++)
            {
                int l=lower_bound(y,y+nn,a[i].y1)-y+1;
                int r=lower_bound(y,y+nn,a[i].y2)-y+1;
                update(1,1,nn,l,r,a[i].flag);
                ans+=len[1]*(a[i+1].x-a[i].x);
            }
            printf("Total explored area: %.2f
    
    ",ans);
        }
        return 0;
    }
    

    第十道:

    HDU 1255 求解矩形并的面积

    这题和上一题处理的代码都一样,我还没变呢就过了……算的时候当然是用覆盖两次以上的算啦!

    在上一题中把线段树节点里面的len域改成覆盖一次和覆盖两次的即可了,覆盖两次以上的肯定有并的了。

    比較难的处理在pushUp那个函数,仅仅要理解了上一题,这一题应该没有那么难理解了。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include<set>
    #include<cmath>
    #include<bitset>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define lson i<<1,l,mid
    #define rson i<<1|1,mid+1,r
    #define INF 510010
    #define maxn 800010
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    double y[2010];
    struct line
    {
        double x,y1,y2;
        int flag;
    } a[2010];
    bool cmp(line a,line b)
    {
        return a.x<b.x;
    }
    double one_len[maxn],more_len[maxn];//覆盖一次或多次
    int lazy[maxn];
    void pushUp(int i,int l,int r)
    {
        if(lazy[i]>1)
            one_len[i]=more_len[i]=y[r-1]-y[l-1];//覆盖两次以上的话。一次和多次的长度是一样的
        else if(lazy[i]==1)
        {
            one_len[i]=y[r-1]-y[l-1];//一次覆盖
            if(l+1==r) more_len[i]=0;//叶子结点多次覆盖定为0
            else more_len[i]=one_len[i<<1]+one_len[i<<1|1];//由于当前已经有覆盖的话。要是以下的也有覆盖一次。那肯定覆盖两次以上啦,画个图就理解了!
        }
        else
        {
            if(l+1==r)
                more_len[i]=one_len[i]=0;//一次覆盖都没有的时候在叶子都为0
            else
            {
                more_len[i]=more_len[i<<1]+more_len[i<<1|1];
                one_len[i]=one_len[i<<1]+one_len[i<<1|1];//往上传
            }
        }
    }
    void update(int i,int l,int r,int L,int R,int c)
    {
        if(r<=L||l>=R) return ;
        if(L<=l&&r<=R)
        {
            lazy[i]+=c;
            pushUp(i,l,r);
            return;
        }
        int mid=(l+r)>>1;
        update(lson,L,R,c);
        update(i<<1|1,mid,r,L,R,c);//建的树不一样
        pushUp(i,l,r);
    }
    int main()
    {
        //freopen("test.txt","r",stdin);
        int t;
        cin>>t;
        while(t--)
        {
            int n;
            scanf("%d",&n);
            int i;
            double x1,y1,x2,y2,ans=0;
            mem(one_len,0);mem(more_len,0);
            mem(lazy,0);
            for(i=0; i<n; i++)
            {
                scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
                y[i<<1]=y1,a[i<<1].x=x1,a[i<<1].y1=y1,a[i<<1].y2=y2,a[i<<1].flag=1;
                y[i<<1|1]=y2,a[i<<1|1].x=x2,a[i<<1|1].y1=y1,a[i<<1|1].y2=y2,a[i<<1|1].flag=-1;
            }
            sort(y,y+n*2); //由于是浮点数,并且数又大。不好建树
            int nn=unique(y,y+n*2)-y;//所以离散化了再建树
            sort(a,a+n*2,cmp);
            for(i=0; i<n*2-1; i++)
            {
                int l=lower_bound(y,y+nn,a[i].y1)-y+1;
                int r=lower_bound(y,y+nn,a[i].y2)-y+1;
                update(1,1,nn,l,r,a[i].flag);
                ans+=more_len[1]*(a[i+1].x-a[i].x);//用多次覆盖的计算
            }
            printf("%.2f
    ",ans);
        }
        return 0;
    }
    


    第十一道:

    HDU 1828 线段树求矩形并的周长

    事实上就是用了上面第九道题的模板就A了。哈哈,太爽了。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include<set>
    #include<cmath>
    #include<bitset>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define lson i<<1,l,mid
    #define rson i<<1|1,mid+1,r
    #define INF 510010
    #define maxn 400010
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    struct line
    {
        int x,y1,y2;
        int flag;
    }a[maxn/7],b[maxn/7];
    bool cmp(line a,line b)
    {
        return a.x<b.x;
    }
    int len[maxn],lazy[maxn],y[maxn/7],x[maxn/7];
    void pushUp(int i,int l,int r,int flag)
    {
        if(lazy[i])
        {
            if(flag) len[i]=y[r-1]-y[l-1];
            else len[i]=x[r-1]-x[l-1];
        }
        else if(l+1==r) len[i]=0;//叶子结点
        else len[i]=len[i<<1]+len[i<<1|1];
    }
    void update(int i,int l,int r,int L,int R,int c,int flag)
    {
        if(r<=L||l>=R) return ;
        if(L<=l&&r<=R)
        {
            lazy[i]+=c;
            pushUp(i,l,r,flag);
            return;
        }
        int mid=(l+r)>>1;
        update(lson,L,R,c,flag);
        update(i<<1|1,mid,r,L,R,c,flag);//建的树不一样
        pushUp(i,l,r,flag);
    }
    int main()
    {
        //freopen("test.txt","r",stdin);
        int n,ii=1;
        while(~scanf("%d",&n))
        {
            int i,x1,y1,x2,y2,ans=0;
            mem(len,0);mem(lazy,0);
            for(i=0;i<n;i++)
            {
                scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
                int l=i<<1,r=i<<1|1;
                y[l]=y1,a[l].x=x1,a[l].y1=y1,a[l].y2=y2,a[l].flag=1;
                y[r]=y2,a[r].x=x2,a[r].y1=y1,a[r].y2=y2,a[r].flag=-1;
                x[l]=x1,b[l].x=y1,b[l].y1=x1,b[l].y2=x2,b[l].flag=1;
                x[r]=x2,b[r].x=y2,b[r].y1=x1,b[r].y2=x2,b[r].flag=-1;
            }
            sort(y,y+n*2); //由于是浮点数,并且数又大,不好建树
            int nn=unique(y,y+n*2)-y;//所以离散化了再建树
            sort(a,a+n*2,cmp);
            int parent=0;
            for(i=0;i<n*2;i++)
            {
                int l=lower_bound(y,y+nn,a[i].y1)-y+1;
                int r=lower_bound(y,y+nn,a[i].y2)-y+1;
                update(1,1,nn,l,r,a[i].flag,1);
                ans+=abs(len[1]-parent);
                parent=len[1];
            }
    
            mem(len,0);mem(lazy,0);
            sort(x,x+n*2); //由于是浮点数,并且数又大,不好建树
            nn=unique(x,x+n*2)-x;//所以离散化了再建树
            sort(b,b+n*2,cmp);
            parent=0;
            for(i=0;i<n*2;i++)
            {
                int l=lower_bound(x,x+nn,b[i].y1)-x+1;
                int r=lower_bound(x,x+nn,b[i].y2)-x+1;
                update(1,1,nn,l,r,b[i].flag,0);
                ans+=abs(len[1]-parent);
                parent=len[1];
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
    

    第十二道:

    POJ 2828 逆序查找空位插入法

    思路:线段树单点更新查找空位插入。

    不理解的能够看这个网址的解释:http://www.cnblogs.com/CheeseZH/archive/2012/04/29/2476134.html

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include<set>
    #include<cmath>
    #include<bitset>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define lson i<<1,l,mid
    #define rson i<<1|1,mid+1,r
    #define INF 510010
    #define maxn 400010
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    int sum[maxn*4],p[maxn],val[maxn],a[maxn],id;
    void build(int i,int l,int r)
    {
        sum[i]=r-l+1;
        if(l==r) return ;
        int mid=(l+r)>>1;
        build(lson);build(rson);
    }
    void update(int i,int l,int r,int v)
    {
        sum[i]--;
        if(l==r) {id=l;return;}
        int mid=(l+r)>>1;
        if(v>sum[i<<1])
        {
            v-=sum[i<<1];
            update(rson,v);
        }
        else update(lson,v);
    }
    int main()
    {
        int n,i;
        while(~scanf("%d",&n))
        {
            build(1,1,n);
            for(i=1;i<=n;i++)
                scanf("%d%d",p+i,val+i);
            for(i=n;i>0;i--)
            {
                update(1,1,n,p[i]+1);
                a[id]=val[i];
            }
            printf("%d",a[1]);
            for(i=2;i<=n;i++)
                printf(" %d",a[i]);
            puts("");
        }
        return 0;
    }
    

    第十三道:

    POJ 2155  二维线段树

    思路:二维线段树就是每一个节点套一棵线段树的树。

    刚開始由于题目是求A[I,J],然后在y查询那直接ans^=Map[i][j]的时候没看懂。后面自己把图画出来了才理解。

    由于仅仅有0和1,所以能够用异或来搞,而不须要每次都须要改动。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include<set>
    #include<cmath>
    #include<bitset>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define lson i<<1,l,mid
    #define rson i<<1|1,mid+1,r
    #define llson j<<1,l,mid
    #define rrson j<<1|1,mid+1,r
    #define INF 510010
    #define maxn 4010
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    bool Map[maxn][maxn];
    int n,q,t,ans;
    void update_y(int i,int j,int l,int r,int y1,int y2)
    {
        if(l==y1&&r==y2) {Map[i][j]^=1;return ;}
        int mid=(l+r)>>1;
        if(y2<=mid) update_y(i,llson,y1,y2);
        else if(y1>mid) update_y(i,rrson,y1,y2);
        else
        {
            update_y(i,llson,y1,mid);
            update_y(i,rrson,mid+1,y2);
        }
    }
    void update_x(int i,int l,int r,int x1,int x2,int y1,int y2)
    {
        if(l==x1&&r==x2)
        {
            update_y(i,1,1,n,y1,y2);
            return ;
        }
        int mid=(l+r)>>1;
        if(x2<=mid) update_x(lson,x1,x2,y1,y2);
        else if(x1>mid) update_x(rson,x1,x2,y1,y2);
        else
        {
            update_x(lson,x1,mid,y1,y2);
            update_x(rson,mid+1,x2,y1,y2);
        }
    }
    void query_y(int i,int j,int l,int r,int y)
    {
        ans^=Map[i][j];
        if(l==r) return ;
        int mid=(l+r)>>1;
        if(y<=mid) query_y(i,llson,y);
        else query_y(i,rrson,y);
    }
    void query_x(int i,int l,int r,int x,int y)
    {
        query_y(i,1,1,n,y);
        if(l==r) return ;
        int mid=(l+r)>>1;
        if(x<=mid) query_x(lson,x,y);
        else query_x(rson,x,y);
    }
    int main()
    {
        //freopen("test.txt","r",stdin);
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&n,&q);
            mem(Map,0);
            while(q--)
            {
                char s[2];
                scanf("%s",s);
                ans=0;
                if(s[0]=='C')
                {
                    int x1,x2,y1,y2;
                    scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
                    update_x(1,1,n,x1,x2,y1,y2);
                }
                else
                {
                    int x,y;
                    scanf("%d%d",&x,&y);
                    query_x(1,1,n,x,y);
                    printf("%d
    ",ans);
                }
            }
            if(t) puts("");
        }
        return 0;
    }
    

    第十四道:

    ZOJ 2859 二维线段树

    思路:自己写的第二发二维线段树1A,哈哈,看来对二维的push操作比較了解了;可是还没遇到在两个线段树中同一时候进行push操作的,事实上这题我是想在x维和y维同一时候进行push操作的。可是想了好久不会,然后看到这题又给出10秒。然后想想在x维线段直接单点查询肯定也过了,然后就仅仅有pushup操作,没有lazy延时pushdown操作。只是也A了。想找题即在二维同一时候进行pushup和pushdown操作的。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include<set>
    #include<cmath>
    #include<bitset>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define lson i<<1,l,mid
    #define rson i<<1|1,mid+1,r
    #define llson j<<1,l,mid
    #define rrson j<<1|1,mid+1,r
    #define INF 0x7fffffff
    #define maxn 1010
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    int Map[maxn][maxn],Min[maxn][maxn];
    int n,q,t,ans;
    void pushup(int i,int j)
    {
        Min[i][j]=min(Min[i][j<<1],Min[i][j<<1|1]);
    }
    void build_y(int i,int u,int j,int l,int r)
    {
        if(l==r)
        {
            Min[i][j]=Map[u][l];
            return ;
        }
        int mid=(l+r)>>1;
        build_y(i,u,llson);build_y(i,u,rrson);
        pushup(i,j);
    }
    void build_x(int i,int l,int r)
    {
        if(l==r)
        {
            build_y(i,l,1,1,n);
            return ;
        }
        int mid=(l+r)>>1;
        build_x(lson);build_x(rson);
    }
    int query_y(int i,int j,int l,int r,int y1,int y2)
    {
        if(l==y1&&y2==r) return Min[i][j];
        int mid=(l+r)>>1;
        if(y2<=mid) return query_y(i,llson,y1,y2);
        else if(y1>mid) return query_y(i,rrson,y1,y2);
        else return min(query_y(i,llson,y1,mid),query_y(i,rrson,mid+1,y2));
    }
    void query_x(int i,int l,int r,int x1,int x2,int y1,int y2)
    {
        if(l==r)
        {
            ans=min(ans,query_y(i,1,1,n,y1,y2));
            return ;
        }
        int mid=(l+r)>>1;
        if(x1<=mid) query_x(lson,x1,x2,y1,y2);
        if(x2>mid) query_x(rson,x1,x2,y1,y2);
    }
    int main()
    {
        freopen("test.txt","r",stdin);
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d",&n);
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    scanf("%d",&Map[i][j]);
            build_x(1,1,n);
            scanf("%d",&q);
            while(q--)
            {
                int x1,y1,x2,y2;
                ans=INF;
                scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
                query_x(1,1,n,x1,x2,y1,y2);
                printf("%d
    ",ans);
            }
        }
        return 0;
    }
    

    HDU 4614

    思路:把区间空的地方记录下来求和。然后1的时候求1到a-1的0个数为cnt。查询cnt+1的位置就是第一个位置。查询cnt+b的位置就是最后一个位置。2的时候直接查询,然后再更新。

    #pragma comment(linker, "/STACK:1024000000,1024000000")
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include<set>
    #include<cmath>
    #include<bitset>
    #include<time.h>
    #define mem(a,b) memset(a,b,sizeof(a))
    #define lson i<<1,l,mid
    #define rson i<<1|1,mid+1,r
    #define llson j<<1,l,mid
    #define rrson j<<1|1,mid+1,r
    #define INF 0x7fffffff
    typedef long long ll;
    typedef unsigned long long ull;
    using namespace std;
    #define maxn 1000005
    int sum[maxn],lazy[maxn];
    void build(int i,int l,int r)
    {
        sum[i]=r-l+1,lazy[i]=0;
        if(l==r) return ;
        int mid=(l+r)>>1;
        build(lson),build(rson);
    }
    void pushDown(int i,int l,int r)
    {
        if(lazy[i]&&l!=r)
        {
            if(lazy[i]==-1) sum[i<<1]=sum[i<<1|1]=0;
            else
            {
                sum[i<<1]=((r-l)>>1)+1;
                sum[i<<1|1]=(r-l+1)>>1;
            }
            lazy[i<<1]=lazy[i<<1|1]=lazy[i];
            lazy[i]=0;
        }
    }
    void pushUp(int i)
    {
        sum[i]=sum[i<<1]+sum[i<<1|1];
    }
    void update(int i,int l,int r,int L,int R,int val)
    {
        if(L<=l&&r<=R)
        {
            if(val==-1) sum[i]=0;
            else sum[i]=r-l+1;
            lazy[i]=val;
            return ;
        }
        pushDown(i,l,r);
        int mid=(l+r)>>1;
        if(R<=mid) update(lson,L,R,val);
        else if(L>mid) update(rson,L,R,val);
        else
        {
            update(lson,L,mid,val);
            update(rson,mid+1,R,val);
        }
        pushUp(i);
    }
    int query(int i,int l,int r,int L,int R)
    {
        if(L<=l&&r<=R) return sum[i];
        int mid=(l+r)>>1,ans=0;
        pushDown(i,l,r);
        if(R<=mid) ans=query(lson,L,R);
        else if(L>mid) ans=query(rson,L,R);
        else ans=query(lson,L,mid)+query(rson,mid+1,R);
        pushUp(i);
        return ans;
    }
    int findPos(int i,int l,int r,int L,int R,int val)
    {
        if(l==r) return l;
        int mid=(l+r)>>1,pos=0;
        pushDown(i,l,r);
        if(R<=mid) pos=findPos(lson,L,R,val);
        else if(L>mid) pos=findPos(rson,L,R,val);
        else
        {
            if(sum[i<<1]>=val) pos=findPos(lson,L,mid,val);
            else pos=findPos(rson,mid+1,R,val-sum[i<<1]);
        }
        pushUp(i);
        return pos;
    }
    int main()
    {
        //freopen("1.txt","r",stdin);
        int t;
        scanf("%d",&t);
        while(t--)
        {
            int n,m,q,a,b;
            scanf("%d%d",&n,&m);
            build(1,1,n);
            while(m--)
            {
                scanf("%d%d%d",&q,&a,&b);
                if(q==1)
                {
                    a++;
                    int tmp=query(1,1,n,a,n);
                    if(!tmp)
                    {
                        puts("Can not put any one.");
                        continue;
                    }
                    if(tmp<b) b=tmp;
                    int sum1=0;
                    if(a-1>=1) sum1=query(1,1,n,1,a-1);
                    int pos1=findPos(1,1,n,1,n,sum1+1);
                    int pos2=findPos(1,1,n,1,n,sum1+b);
                    update(1,1,n,pos1,pos2,-1);
                    printf("%d %d
    ",pos1-1,pos2-1);
                }
                else
                {
                    a++,b++;
                    int sum1=query(1,1,n,a,b);
                    update(1,1,n,a,b,1);
                    printf("%d
    ",b-a+1-sum1);
                }
            }
            puts("");
        }
        return 0;
    }
    




  • 相关阅读:
    less和vim中使用正则表达式搜索
    正则表达式解英语单词字谜
    正则表达式中的Quantifiers
    grep正则表达式(二)
    grep正则表达式(一)
    Linux中的touch命令总结(一)
    find命令进阶(三):xargs
    find命令进阶(二):对找到的文件执行操作exec
    构造函数
    Bean的生命周期
  • 原文地址:https://www.cnblogs.com/bhlsheji/p/5104440.html
Copyright © 2011-2022 走看看