zoukankan      html  css  js  c++  java
  • 树状数组的应用

    树状数组是一种较为高级的数据结构,下面总结一下具体应用(按照树神讲的顺序:

    树状数组Binary Indexed Trees简称BIT。其平摊了修改和查询的时间复杂度,使他们都成为O(logN)的级别!

    1.单点修改区间查询。(大家都会不再赘述。

    2.区间修改单点查询,然后利用树状数组进行差分的求和,然后即可实现区间修改。

    伪代码:add(x,1);add(y+1,-1);x,y为区间修改。

    3.单点修改加求区间第k小的数字,很不好写呢,细节比较重要,wa了3次。poj 2985

    #include<bits/stdc++.h>
    #include<iostream>
    #include<cmath>
    #include<ctime>
    #include<cstdio>
    #include<iomanip>
    #include<cstring>
    #include<string>
    #include<stack>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include<deque>
    #include<vector>
    #include<set>
    using namespace std;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    void put(int x)
    {
        if(x==0){putchar('0');putchar('
    ');return;}
        if(x<0)putchar('-'),x=-x;
        int num=0;char ch[50];
        while(x)ch[++num]=x%10+'0',x/=10;
        while(num)putchar(ch[num--]);
        putchar('
    ');return;
    }
    const int maxn=200002;
    int a[maxn],c[maxn],f[maxn];
    int n,m;
    int getfather(int x){return x==f[x]?x:f[x]=getfather(f[x]);}
    void add(int x,int y){for(;x;x-=x&(-x))c[x]+=y;return;}
    int ask(int x){int ans=0;for(;x<=n;x+=x&(-x))ans+=c[x];return ans;}
    int main()
    {
        //freopen("1.in","r",stdin);
        n=read();m=read();
        for(int i=1;i<=n;i++)f[i]=i,a[i]=1,add(a[i],1);
        for(int i=1;i<=m;i++)
        {
            int p,x,y;
            p=read();
            if(p==0)
            {
                x=read();y=read();
                int xx=getfather(x);
                int yy=getfather(y);
                if(xx!=yy)
                {
                    f[xx]=yy;
                    add(a[xx],-1);//wa的原因
                    add(a[yy],-1);//让他们的父亲减
                    a[yy]+=a[xx];a[xx]=0;
                    add(a[yy],1);
                }
                else continue;
            }
            if(p==1)
            {
                x=read();
                int l=1,r=n;
                while(l+1<r)
                {
                    int mid=(l+r)>>1;
                    if(ask(mid)>=x)l=mid;//寻找那个刚好合适的人呢
                    else r=mid;//一直寻找
                }
                if(ask(r)>=x)put(r);//r比l更优注意~!
                else put(l);
            }
        }
        return 0;
    }
    View Code

    当然还有一种二进制的方法直接得出不过需要转换一下,本人还不懂..

    #include<bits/stdc++.h>
    #include<iostream>
    #include<cmath>
    #include<ctime>
    #include<cstdio>
    #include<iomanip>
    #include<cstring>
    #include<string>
    #include<stack>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include<deque>
    #include<vector>
    #include<set>
    using namespace std;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    void put(int x)
    {
        if(x==0){putchar('0');putchar('
    ');return;}
        if(x<0)putchar('-'),x=-x;
        int num=0;char ch[50];
        while(x)ch[++num]=x%10+'0',x/=10;
        while(num)putchar(ch[num--]);
        putchar('
    ');return;
    }
    const int maxn=200002;
    int a[maxn],c[maxn],f[maxn];
    int n,m,sum;
    int getfather(int x){return x==f[x]?x:f[x]=getfather(f[x]);}
    void add(int x,int y){for(;x<=n;x+=x&(-x))c[x]+=y;return;}
    int ask(int x){int ans=0;for(;x<=n;x+=x&(-x))ans+=c[x];return ans;}
    int main()
    {
        //freopen("1.in","r",stdin);
        n=read();m=read();sum=n;
        for(int i=1;i<=n;i++)f[i]=i,a[i]=1,add(a[i],1);
        for(int i=1;i<=m;i++)
        {
            int p,x,y;
            p=read();
            if(p==0)
            {
                x=read();y=read();
                int xx=getfather(x);
                int yy=getfather(y);
                if(xx!=yy)
                {
                    f[xx]=yy;
                    add(a[xx],-1);//wa的原因
                    add(a[yy],-1);//让他们的父亲减
                    a[yy]+=a[xx];a[xx]=0;
                    add(a[yy],1);
                    sum--;//还有几组
                }
                else continue;
            }
            if(p==1)
            {
                x=read();
                int ans=0,cnt=0;int u=sum-x+1;//卡好范围第u小
                for(int i=20;i>=0;i--)
                {
                    ans+=(1<<i);
                    if(ans>n||cnt+c[ans]>=u)ans-=(1<<i);
                    else cnt+=c[ans];
                }
                put(ans+1);
            }
        }
        
        return 0;
    }
    View Code

    4.可以进行二维的树状数组,也就是一维变二维;

    二维的树状数组罢了其实很简单。vijos 1512;

    #include<iomanip>
    #include<iostream>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<map>
    #include<algorithm>
    #include<ctime>
    #include<vector>
    #include<queue>
    #include<deque>
    #include<set>
    #include<stack>
    using namespace std;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    void put(int x)
    {
        if(x==0)
        {
            putchar('0');
            putchar('
    ');
            return;
        }
        if(x<0)
        {
            putchar('-');
            x=-x;
        }
        int num=0;char ch[25];
        while(x)ch[++num]=x%10+'0',x/=10;
        while(num)putchar(ch[num--]);
        putchar('
    ');
    }
    const int maxn=1200;
    int n,p;
    int c[maxn][maxn];
    void add(int x,int y,int z)
    {
        for(int i=x;i<=n;i+=i&(-i))
            for(int j=y;j<=n;j+=j&(-j))
                c[i][j]+=z;
    }
    int ask(int x,int y)
    {
        int ans=0;
        for(int i=x;i;i-=i&(-i))
            for(int j=y;j;j-=j&(-j))
                ans+=c[i][j];
        return ans;
    }
    int main()
    {
        //freopen("1.in","r",stdin);
        n=read()+1;
        while(1)
        {
            p=read();
            if(p==3)break;
            if(p==1)
            {
                int x,y,k;
                x=read()+1;y=read()+1;k=read();
                add(x,y,k);
            }
            if(p==2)
            {
                int x,x1,s,s1;
                x=read()+1;x1=read()+1;s=read()+1;s1=read()+1;
                put(ask(s,s1)-ask(s,x1-1)-ask(x-1,s1)+ask(x-1,x1-1));
            }
        }
        return 0;
    }
    View Code

    5.图腾计数树状数组应用 CH4201

    数出左边比当前点高的和低和比右边点高或低的相乘累加即可。一遍a~!

    #include<bits/stdc++.h>
    #include<iomanip>
    #include<iostream>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<map>
    #include<algorithm>
    #include<ctime>
    #include<vector>
    #include<queue>
    #include<deque>
    #include<set>
    #include<stack>
    using namespace std;
    inline long long read()
    {
        long long x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    void put(long long x)
    {
        if(x==0)
        {
            putchar('0');
            putchar(' ');
            return;
        }
        if(x<0)
        {
            putchar('-');
            x=-x;
        }
        long long num=0;char ch[60];
        while(x)ch[++num]=x%10+'0',x/=10;
        while(num)putchar(ch[num--]);
        putchar(' ');
    }
    const long long maxn=200002;
    long long a[maxn],c[maxn],u[maxn],r[maxn],l[maxn];
    long long w[maxn],y[maxn];
    long long n,ans=0,num=0;
    void add(long long x,long long y){for(;x<=n;x+=x&(-x))c[x]+=y;return;}
    long long ask(long long x){long long ans=0;for(;x;x-=x&(-x))ans+=c[x];return ans;}
    void add1(long long x,long long y){for(;x;x-=x&(-x))u[x]+=y;return;}
    long long ask1(long long x){long long ans=0;for(;x<=n;x+=x&(-x))ans+=u[x];return ans;}
    int main()
    {
        //freopen("1.in","r",stdin);
        n=read();
        for(long long i=1;i<=n;i++)
        {
            a[i]=read();
            add(a[i],1);
            add1(a[i],1);
            l[i]=ask1(a[i]+1);
            w[i]=ask(a[i]-1);
        }
        memset(u,0,sizeof(u));
        memset(c,0,sizeof(c));
        for(long long i=n;i>=1;i--)
        {
            add(a[i],1);
            add1(a[i],1);
            r[i]=ask1(a[i]+1);
            y[i]=ask(a[i]-1);
        }
        for(long long i=1;i<=n;i++)
        {
            ans+=l[i]*r[i];
            num+=w[i]*y[i];
        }
        put(ans);put(num);
        return 0;
    }
    View Code

    关于逆序对的求解在另一篇博文之中,本文不再赘述。

    6.二维数点

    直接转换一下可以考虑排序如果区间无序的话,然后进行数点即可。

    #include<bits/stdc++.h>
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<cmath>
    #include<iomanip>
    #include<ctime>
    #include<queue>
    #include<map>
    #include<vector>
    #include<stack>
    #include<algorithm>
    using namespace std;
    
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    
    const int maxn=60002;
    int n,c[maxn],maxx;
    int x[maxn],y[maxn];
    
    void add(int x,int y){for(;x<=maxx;x+=x&(-x))c[x]+=y;}
    
    int ask(int x){int ans=0;for(;x;x-=x&(-x))ans+=c[x];return ans;}
    
    int main()
    {
        //freopen("1.in","r",stdin);
        n=read();
        for(int i=1;i<=n;i++)
        {
            x[i]=read();y[i]=read();
            x[i]+=1;y[i]+=1;
            maxx=max(maxx,x[i]);
            
        }
        for(int i=1;i<=n;i++)
        {
            add(x[i],1);
            printf("%d
    ",ask(x[i])-1);
        }
        return 0;
    }
    View Code

    8.一道比较有思维难度的题

    很朴实的题,除了范围有点大似乎没什么不好这个也其实很简单,具体细节看代码。

    #include<bits/stdc++.h>
    #include<iomanip>
    #include<iostream>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<map>
    #include<algorithm>
    #include<ctime>
    #include<vector>
    #include<queue>
    #include<deque>
    #include<set>
    #include<stack>
    using namespace std;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    const int maxn=1000008;
    struct wy
    {
        int x,y;
        int an,id;
    }t[maxn];
    int n,m;
    int a[maxn],pre[maxn];//prefix前缀。
    int cmp(wy x,wy y){if(x.x==y.x)return x.y<y.y;return x.y<y.y;}
    int my(wy x,wy y){return x.id<y.id;}
    int c[maxn],ans=0;
    void add(int x,int y){for(;x<=n;x+=x&(-x))c[x]+=y;return;}
    int ask(int x){int ans=0;for(;x;x-=x&(-x))ans+=c[x];return ans;}
    void put(int x)
    {
        if(x==0)
        {
            putchar('0');
            putchar('
    ');
            return;
        }
        if(x<0)
        {
            putchar('-');
            x=-x;
        }
        int num=0;char ch[16];
        while(x)ch[++num]=x%10+'0',x/=10;
        while(num)putchar(ch[num--]);
        putchar('
    ');
    }
    int main()
    {
        //freopen("1.in","r",stdin);
        n=read();
        for(int i=1;i<=n;i++)a[i]=read()+1;
        m=read();
        for(int i=1;i<=m;i++)t[i].x=read(),t[i].y=read(),t[i].id=i;
        sort(t+1,t+1+m,cmp);
        //for(int i=1;i<=m;i++)printf("%d %d
    ",t[i].x,t[i].y);
        for(int i=1,j=1;i<=n;i++)
        {
            if(pre[a[i]]==0)
            {
                add(i,1);
                pre[a[i]]=i;
            }
            else 
            {
                add(pre[a[i]],-1);
                add(i,1);
                pre[a[i]]=i;
            }
            while(i==t[j].y)
            {
                t[j].an=ask(t[j].y)-ask(t[j].x-1);
                j++;
            }
        }
        sort(t+1,t+1+m,my);
        for(int i=1;i<=m;i++)put(t[i].an);
        return 0;
    }
    View Code

    最后:

    注意:

    因为lowbit(0)=0所以树状数组的下标只能从1开始,如果题干中出现0的情况,把所有输入数据都+1是个不错的解决方法。

    树状数组可以做到区间修改,区间查询,方法为两个树状数组维护原序列。

    树状数组的求和操作适用于所有满足结合律的操作,例如异或。

    谁道闲情抛掷久,每到春来,惆怅还依旧。

  • 相关阅读:
    js收集
    SQL 收集
    Char 和 Varchar 与 nchar 和 nvarchar 最终总结比较
    VS 2005 Debugger crashing with IE 8
    ref与out区别(ref有进有出,而out只出不进)
    Gridview利用DataFormatString属性设置数据格式
    [原创]修改TFS本地文件映射路径,无法映射到相同文件夹问题。
    [转载] DataManipulator.Filter 筛选数据(图表控件)
    Chart(MSChart)基本属性及用法介绍
    Word乱码解决方式
  • 原文地址:https://www.cnblogs.com/chdy/p/10087282.html
Copyright © 2011-2022 走看看