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

    反对暴力, 提倡和谐!

  • 相关阅读:
    字符串替换
    字符串查找
    字符串比较
    字节与字符串相互转换
    1365. How Many Numbers Are Smaller Than the Current Number
    1486. XOR Operation in an Array
    1431. Kids With the Greatest Number of Candies
    1470. Shuffle the Array
    1480. Running Sum of 1d Array
    【STM32H7教程】第56章 STM32H7的DMA2D应用之刷色块,位图和Alpha混合
  • 原文地址:https://www.cnblogs.com/acm1314/p/4539463.html
Copyright © 2011-2022 走看看