zoukankan      html  css  js  c++  java
  • 线段树与树状数组草稿

    http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=604&pid=1002

    Dylans loves sequence

    Accepts: 249
    Submissions: 806
    Time Limit: 2000/1000 MS (Java/Others)
    Memory Limit: 131072/131072 K (Java/Others)
    Problem Description

    Dylans is given N 
    numbers a[1]....a[N

    And there are
    questions.

    Each question is like this (L,R

    his goal is to find the “inversions” from number
    to number
    .

    more formally,his needs to find the numbers of pair(x,y 
    ), that Lx,y
    and x<y 
    and a[x]>a[y

    Input

    In the first line there is two numbers N 
    and
    .

    Then in the second line there are N 
    numbers:a[1]..a[N

    In the next
    lines,there are two numbers L,
    in each line.

    N1000,Q100000,LR,1a[i]31 

    Output

    For each query,print the numbers of "inversions”

    Sample Input
    3 2
    3 2 1
    1 2
    1 3
    Sample Output
    1
    3
    Hint
    You shouldn't print any space in each end of the line in the hack data.
     动态规划写法:
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdlib>
    #include <cstdio>
    #include <string>
    #include <vector>
    #include <cmath>
    using namespace std;
    int dp[1005][1005],dp2[1005][1005];
    int main()
    {
        int n,Q;
        while(scanf("%d%d",&n,&Q)==2)
        {
            int a[1005];
            for(int i=0;i<n;++i) scanf("%d",&a[i]);
            for(int i=0;i<n;++i)
            {
                dp[i][i]=0;
                for(int j=i+1;j<n;++j)
                {
                    dp[i][j]=dp[i][j-1];
                    if(a[j]<a[i]) dp[i][j]++;
                }
            }
            for(int i=0;i<n;++i)
            {
                dp2[i][0]=dp[0][i];
                for(int j=1;j<=i;++j) dp2[i][j]=dp2[i][j-1]+dp[j][i];
            }
            while(Q--)
            {
                int l,r;
                scanf("%d%d",&l,&r);
                l--;r--;
                if(l==0) printf("%d
    ",dp2[r][r]);
                else printf("%d
    ",dp2[r][r]-dp2[r][l-1]);
            }
        }
        return 0;
    }
    //树状数组写法
    #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int i,j,m,n,p,k,a[1005],q,b[1005],tree[1005],l,r,ans[1005][1005]; int lowbit(int x) { return x&-x; } int ask(int x) { int sum=0; for (;x;x-=lowbit(x)) sum+=tree[x]; return sum; } int ins(int x) { for (;x<=b[0];x+=lowbit(x)) tree[x]++; } int main() { scanf("%d%d",&n,&q); for (i=1;i<=n;++i) scanf("%d",&a[i]),b[i]=a[i]; sort(b+1,b+n+1); b[0]=unique(b+1,b+n+1)-(b+1); for (i=1;i<=n;++i) a[i]=lower_bound(b+1,b+b[0]+1,a[i])-b; for (i=1;i<=n;++i) { memset(tree,0,sizeof(tree)); for (j=i;j<=n;++j) { ans[i][j]=ans[i][j-1]+ask(b[0])-ask(a[j]); ins(a[j]); } } for (;q--;) { scanf("%d%d",&l,&r); printf("%d ",ans[l][r]); } }

    线段树: 

    《1》http://poj.org/problem?id=3264(简单题,练模板)

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    using namespace std;
    
    const int MAXN = 200005;
    const int INF = 1000000000;
    int nMax, nMin;
    
    struct Node{
        int L, R;
        int nMin, nMax;
    }segTree[MAXN*3];
    
    int a[MAXN];
    
    void Build(int i, int L, int R)
    {
        segTree[i].L = L;
        segTree[i].R = R;
        if(L==R)
        {
            segTree[i].nMin = segTree[i].nMax = a[L];
            return;
        }
        int mid = (L+R)>>1;
        Build(i<<1, L, mid);
        Build(i<<1|1, mid+1, R);
        segTree[i].nMin=min(segTree[i<<1].nMin, segTree[i<<1|1].nMin);
        segTree[i].nMax=max(segTree[i<<1].nMax, segTree[i<<1|1].nMax);
    }
    void Query(int i, int L, int R)
    {
        if(segTree[i].nMax<=nMax&&segTree[i].nMin>=nMin) return;
        if(segTree[i].L==L&&segTree[i].R==R)
        {
            nMax = max(segTree[i].nMax, nMax);
            nMin = min(segTree[i].nMin, nMin);
            return;
        }
        int mid = (segTree[i].L+segTree[i].R)>>1;
        if(R<=mid) Query(i<<1, L, R);
        else if(L>mid) Query(i<<1|1, L, R);
        else
        {
            Query(i<<1, L, mid);
            Query(i<<1|1, mid+1, R);
        }
    }
    
    int main()
    {
        int n, q, L, R, i;
        while(scanf("%d%d", &n, &q)!=EOF)
        {
            for(i=1; i<=n; i++)
            scanf("%d", &a[i]);
            Build(1, 1, n);
            for(i=1; i<=q; i++)
            {
                scanf("%d%d", &L, &R);
                nMax = -INF; nMin = INF;
                Query(1, L, R);
                printf("%d
    ", nMax-nMin);
            }
        }
        return 0;
    }
    View Code

    《2》http://acm.hdu.edu.cn/showproblem.php?pid=1166(简单题, 用模板)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int MAXN = 50000+5;
    int num[MAXN];
    
    struct Node{
        int l, r;
        int nSum;
    }segTree[MAXN*3];
    
    void Build(int i, int l, int r)
    {
        segTree[i].l = l;
        segTree[i].r = r;
        if(l==r)
        {
            segTree[i].nSum=num[l];
            return;
        }
        int mid=(l+r)>>1;
        Build(i<<1, l, mid);
        Build(i<<1|1, mid+1, r);
        segTree[i].nSum=segTree[i<<1].nSum+segTree[i<<1|1].nSum;
    }
    
    void Add(int i, int t, int b)
    {
        segTree[i].nSum+=b;
        if(segTree[i].l==t&&segTree[i].r==t) return;
        int mid=(segTree[i].l+segTree[i].r)>>1;
        if(t<=mid) Add(i<<1, t, b);
        else Add(i<<1|1, t, b);
    }
    
    int Query(int i, int l, int r)
    {
        if(segTree[i].l==l&&segTree[i].r==r)
        return segTree[i].nSum;
        int mid=(segTree[i].l+segTree[i].r)>>1;
        if(r<=mid) return Query(i<<1, l, r);
        else if(l>mid) return Query(i<<1|1, l, r);
        else return Query(i<<1, l, mid)+Query(i<<1|1, mid+1, r);  
    }
    
    int main()
    {
        int T;
        int kase;
        int n, i;
        char str[10];
        int a, b;
        scanf("%d", &T);
        for(kase=1; kase<=T; kase++)
        {
            scanf("%d", &n);
            for(i=1; i<=n; i++)
            scanf("%d", &num[i]);
            Build(1, 1, n);
            printf("Case %d:
    ", kase);
            while(scanf("%s", &str))
            {
                if(str[0]=='E') break;
                //if(strcmp(str,"End")==0) break;
                scanf("%d%d", &a, &b);
                if(str[0]=='A') Add(1, a, b);
                //if(strcmp(str, "Add")==0) Add(1, a, b);
                else if(str[0]=='S') Add(1, a, -b);
                //else if(strcmp(str, "Sub")==0) Add(1, a, -b);
                else printf("%d
    ", Query(1, a, b));
            }
        }
        return 0;
    }
    View Code

    《3》http://poj.org/problem?id=3468(进阶题)

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    using namespace std;
    
    const int MAXN = 100000;
    int num[MAXN];
    struct Node{
        int l, r;
        long long nSum;
        long long Inc;
    }segTree[MAXN*3];
    
    void Build(int i, int l, int r)//建立线段树 
    {
        segTree[i].l=l;
        segTree[i].r=r;
        segTree[i].Inc=0;
        if(l==r)
        {
            segTree[i].nSum=num[l];
            return;
        }
        int mid=(l+r)>>1;
        Build(i<<1, l, mid);
        Build(i<<1|1, mid+1, r);
        segTree[i].nSum=segTree[i<<1].nSum+segTree[i<<1|1].nSum;
    }
    void Add(int i, int a, int b, long long c)//在结点i的区间(a,b)上增加c 
    {
        if(segTree[i].l==a&&segTree[i].r==b)//这个过程和建树过程相似。 
        {
            segTree[i].Inc+=c;
            return;
        }
        segTree[i].nSum+=c*(b-a+1);
        int mid=(segTree[i].l+segTree[i].r)>>1;
        if(b<=mid) Add(i<<1, a, b, c);
        else if(a>mid) Add(i<<1|1, a, b, c);
        else
        {
            Add(i<<1, a, mid, c);
            Add(i<<1|1, mid+1, b, c);
        }
    }
    
    long long Query(int i, int a, int b)//查询a-b的总和 
    {
        if(segTree[i].l==a&&segTree[i].r==b)
        {
            return segTree[i].nSum+(b-a+1)*segTree[i].Inc;
        }
        segTree[i].nSum+=(segTree[i].r-segTree[i].l+1)*segTree[i].Inc;
        int mid=(segTree[i].l+segTree[i].r)>>1;
        Add(i<<1, segTree[i].l, mid, segTree[i].Inc);
        Add(i<<1|1, mid+1, segTree[i].r, segTree[i].Inc);
        segTree[i].Inc = 0;
        if(b<=mid) return Query(i<<1, a, b);
        else if(a>mid) return Query(i<<1|1, a, b);
        else return Query(i<<1, a, mid)+Query(i<<1|1, mid+1, b);
    }
    int main()
    {
        int n, q;
        int i;
        int a, b, c;
        char ch;
        while(scanf("%d%d", &n, &q)!=EOF)
        {
            for(i=1; i<=n; i++)
            scanf("%d", &num[i]);
            Build(1, 1, n);
            for(i=1; i<=q; i++)
            {
                cin>>ch;
                if(ch=='C')
                {
                    scanf("%d%d%d", &a, &b, &c);
                    Add(1, a, b, c);
                }
                else
                {
                    scanf("%d%d", &a, &b);
                    printf("%I64d
    ", Query(1, a, b));
                }
            }
        }
        return 0;
    }
    View Code

    《4》http://acm.hdu.edu.cn/showproblem.php?pid=1754(简单题)

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int MAXN = 200000+10;
    int num[MAXN];
    
    struct Node
    {
        int l, r;
        int nMax;
    }segTree[MAXN*4];
    
    void Build(int i, int l, int r)
    {
        segTree[i].l=l;
        segTree[i].r=r;
        if(l==r)
        {
            segTree[i].nMax=num[l];
            return;
        }
        int mid=(segTree[i].l+segTree[i].r)>>1;
        Build(i<<1, l, mid);
        Build(i<<1|1, mid+1, r);
        segTree[i].nMax=max(segTree[i<<1].nMax, segTree[i<<1|1].nMax);
    }
    
    void Update(int i, int l, int r, int x)
    {
        if(segTree[i].l==l&&segTree[i].r==r)
        {
            segTree[i].nMax=x;
            return;
        }
        int mid=(segTree[i].l+segTree[i].r)>>1;
        if(r<=mid)
            Update(i<<1, l, r, x);
        else if(l>mid)
            Update(i<<1|1, l, r, x);
        else
        {
            Update(i<<1, l, mid, x);
            Update(i<<1|1, mid+1, r, x);
        }
        segTree[i].nMax=max(segTree[i<<1].nMax, segTree[i<<1|1].nMax);
    }
    
    int Query(int i, int l, int r)
    {
        if(segTree[i].l==l&&segTree[i].r==r)
        {
            return segTree[i].nMax;
        }
        int mid=(segTree[i].l+segTree[i].r)>>1;
        if(r<=mid)
            return Query(i<<1, l, r);
        else if(l>mid)
            return Query(i<<1|1, l, r);
        else 
            return max(Query(i<<1, l, mid), Query(i<<1|1, mid+1, r));
    }
    
    int main()
    {
        int n, m;
        while(scanf("%d%d", &n, &m)!=EOF)
        {
            int i, j, k, a, b;
            char s[2];
            for(i=1; i<=n; i++)
                scanf("%d", &num[i]);
            Build(1, 1, n);
            for(i=0; i<m; i++)
            {
                scanf("%s%d%d", s, &a, &b);
                if(s[0]=='U')
                    Update(1, a, a, b);
                else
                    printf("%d
    ", Query(1, a, b));
            }
        }
        return 0;
    }
    View Code

    《5》http://acm.hdu.edu.cn/showproblem.php?pid=2795

    线段树, 适用的范围还真广!!。 这个题看到别人能用线段树切, 也是非常的机智。

    /*以墙的高度和广告数量的较小值建线段树 ,
    树中v表示该段中最大空余量,idid表示最大
    空余量所在行 (从上开始记)。每次贴广告
    先比较空余量, 从上到下,空余足够就贴, 
    再根据查找到的空余行进行点更新。。。 
    */ 
    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    using namespace std;
    
    const int MAXN = 200000+10;
    int h, v, n;
    
    struct Node
    {
        int l, r;
        int id;
        int v;
    }segTree[MAXN*4];
    
    void Build(int i, int l, int r)
    {
        segTree[i].l = l;
        segTree[i].r = r;
        segTree[i].id = l;
        segTree[i].v = v;
        if(l==r)
            return;
        int mid=(l+r)>>1;
        Build(i<<1, l, mid);
        Build(i<<1|1, mid+1, r);
    }
    
    int Query(int i, int val)
    {
        if(segTree[i].v<val)
            return -1;
        if(segTree[i].l==segTree[i].r)
            return segTree[i].id;
        if(segTree[i<<1].v>=val)
            return Query(i<<1, val);
        else
            return Query(i<<1|1, val);
    }
    
    void Update(int i, int l, int r, int val)
    {
        if(segTree[i].l==l&&segTree[i].r==r)
        {
            segTree[i].v-=val;
            return;
        }
        int mid=(segTree[i].l+segTree[i].r)>>1;
        if(r<=mid)
            Update(i<<1, l, r, val);
        else if(l>mid)
            Update(i<<1|1, l, r, val);
        if(segTree[i<<1].v>segTree[i<<1|1].v)
        {
            segTree[i].v = segTree[i<<1].v;
            segTree[i].id = segTree[i<<1].id;
        }
        else 
        {
            segTree[i].v = segTree[i<<1|1].v;
            segTree[i].id = segTree[i<<1|1].id;
        }
    }
    
    int main()
    {
        while(scanf("%d%d%d", &h, &v, &n)!=EOF)
        {
            int j, k;
            if(h>n)
                h = n;
            Build(1, 1, h);
            for(int q=0; q<n; q++)
            {
                scanf("%d", &k);
                int t;
                t = Query(1, k);
                printf("%d
    ",t);  
                if(t!=-1)  
                Update(1, t, t, k);  
            }
        }
        return 0;
    }
    View Code

    《6》http://acm.hdu.edu.cn/showproblem.php?pid=1394(求逆序数)

    线段树法:

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    
    const int MAXN=5000+10;
    int a[MAXN];
    
    struct Node
    {
        int l,r;
        int sum;
    }segTree[MAXN*3];
    
    void Build(int i,int l,int r)
    {
        segTree[i].l=l;
        segTree[i].r=r;
        if(l==r)
        {
            segTree[i].sum=0;
            return;
        }
        int mid=(l+r)>>1;
        Build(i<<1,l,mid);
        Build((i<<1)|1,mid+1,r);
        segTree[i].sum=0;
    }
    
    void add(int i,int t,int val)
    {
        segTree[i].sum+=val;
        if(segTree[i].l==segTree[i].r)
        {
            return;
        }
        int mid=(segTree[i].l+segTree[i].r)>>1;
        if(t<=mid) add(i<<1,t,val);
        else add((i<<1)|1,t,val);
    }
    
    int sum(int i,int l,int r)
    {
        if(segTree[i].l==l&&segTree[i].r==r)
          return segTree[i].sum;
        int mid=(segTree[i].l+segTree[i].r)>>1;
        if(r<=mid) return sum(i<<1,l,r);
        else if(l>mid)  return sum((i<<1)|1,l,r);
        else return sum(i<<1,l,mid)+sum((i<<1)|1,mid+1,r);
    }
    
    int main()
    { 
        int n;
        while(scanf("%d",&n)!=EOF)
        {
            Build(1,0,n-1);
            for(int i=0;i<n;i++)
              scanf("%d",&a[i]);
            int ans=0;
            for(int i=0;i<n;i++)
            {
                ans+=sum(1,a[i],n-1);
                add(1,a[i],1);
            }
            int Min=ans;
            for(int i=0;i<n;i++)
            {
                ans-=a[i];//减少的逆序数
                ans+=n-a[i]-1;//增加的逆序数 
                if(ans<Min)Min=ans;
            }
            printf("%d
    ",Min);
        }
        return 0;
    }
    View Code

    线段树求逆序数, 其实就是将输入的数组元素依次插入线段树, 线段树的段值sum表示该段中以插入的数字个数。每次插入,计算已插入的比插入数大的数的个数,总和就是逆序数。
    树状数组法:(先占个坑, 嘿嘿!)

    《7》http://poj.org/problem?id=2528(线段树+离散)

    这道题看似并不复杂,然而此坑甚深。 本来想暴力一下的, 结果,,,,, 其实大多数的能用线段树解决的问题,都有比较简单的暴力思路, 然而大都卡时间。这就体现出线段树的高效性啦! 。 这也是为何算法令人如痴如醉的一个原因-------------高效性。

    暴力该打:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<set>
    using namespace std;
    
    const int MAXN = 10000000+10;
    int a[MAXN];
    
    int main()
    {
        int T, n;
        int l, r;
        scanf("%d", &T);
        while(T--)
        {
            set<int> ss;
            memset(a, 0, sizeof(a));
            scanf("%d", &n);
            for(int i=1; i<=n; i++)
            {
                scanf("%d%d", &l, &r);
                for(int j=l; j<=r; j++)
                a[j] = i;
            }
            for(int i=0; i<MAXN; i++)
            ss.insert(a[i]);
            printf("%d
    ",ss.size()-1);
        }
        return 0;
    }
    View Code

    反对暴力, 提倡和谐!

  • 相关阅读:
    合并本地多次commit为一个commit
    git 取消文件跟踪
    遍历进程 遍历窗口
    linux查看程序运行参数
    ubuntu下载地址
    将博客搬至CSDN
    extern"C"的使用
    ESP32-NVS存储(非易失性存储库)
    ESP32-EEPROM存储
    c语言简单数据类型
  • 原文地址:https://www.cnblogs.com/acm1314/p/4539463.html
Copyright © 2011-2022 走看看