树状数组总结篇

(本来对于树状数组没啥感觉的....毕竟潜意识的认为只要树状数组的能够解决的问题我都可以用线段树解决,但是对于一些玄学题目可能会被卡掉T 因为在树状数组能适用的题,他在空间和时间都优于线段树,所以学好树状数组也是很重要的,具体的实现过程百度.....实质上只是二进制的运用 熟练运用二进制就能解决相关问题;

传送门http://poj.org/problem?id=3067

题意:在日本的东海岸和西海岸修交通线,问这些交通线有多少交叉点;

解法:按照x坐标升序,相同的情况下y升序排序,然后查询在之前有多少满足条件的即可

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#define N 1005
#define ll long long
using namespace std;
typedef struct node{
    int x;int y;
    friend bool operator <(node a,node b){
        if(a.x==b.x)  return a.y<b.y;
        else return a.x<b.x;
    }
}node;
node a[N*N];
ll d[N];
int get_id(int x){
    return x&(-x);
}
void update(int x){
    while(x<=N){
        d[x]++;x+=get_id(x);
    }
}
ll Sum(int x){
  ll ans=0;
  while(x>0){
    ans+=d[x];x-=get_id(x);
  }
  return ans;
}
int main(){
    int T;scanf("%d",&T);
    int n,m,k;int Case=0;
    while(T--){
        scanf("%d%d%d",&n,&m,&k);
        int aa,bb;
        memset(d,0,sizeof(d));
        for(int i=1;i<=k;i++){
            scanf("%d%d",&a[i].x,&a[i].y);
        }
        sort(a+1,a+k+1);
        update(a[1].y);
        ll sum=0;
       for(int i=2;i<=k;i++){
         sum+=(i-1-Sum(a[i].y));
         update(a[i].y);
       }
       printf("Test case %d: ",++Case);
       printf("%lld\n",sum);
    }
    return 0;
}

 

传送门 http://poj.org/problem?id=3321

题意:给你一颗苹果树,每个节点初始都有一个苹果,执行一些操作(有苹果的直接拿掉 没有的直接补上去一个苹果),然后查询这个节点的子树上有多少个苹果;

解法:第一反应树剖+树状数组查询区间和(似乎有更简单的树状数组 并不会2333333)

#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#define N 100005
using namespace std;
int pos;int son[N];int deep[N];int a[N];
int first[N*2];int Next[N*2];int vi[N*2];
void intx(){
    pos=0;
    deep[0]=0;
    for(int i=1;i<N;i++)   first[i]=-1;
    for(int i=1;i<N;i++) son[i]=-1;
}
int fa[N];int num[N];
void dfs1(int v,int pre){
    fa[v]=pre;num[v]=1;deep[v]=deep[pre]+1;
    for(int i=first[v];i!=-1;i=Next[i]){
        if(vi[i]!=pre){
            dfs1(vi[i],v);
            num[v]+=num[vi[i]];
            if(son[v]==-1||num[son[v]]<num[vi[i]]){
                son[v]=vi[i];
            }
        }
    }
}
int p[N];int tp[N];int fp[N];
void dfs2(int v,int td){
    p[v]=++pos;fp[p[v]]=v;tp[v]=td;
    if(son[v]!=-1) dfs2(son[v],td);
    for(int i=first[v];i!=-1;i=Next[i]){
        if(vi[i]!=fa[v]&&vi[i]!=son[v]) dfs2(vi[i],vi[i]);
    }
}
int d[N];
int get_id(int x){
    return x&(-x);
}
int n;
void get_d(int n){
    for(int i=1;i<=n;i++){
        int j=i;
        while(j<=n){
            d[j]+=a[i];j+=get_id(j);
        }
    }
}
void update(int x,int t){
    while(x<=n){
        d[x]+=t;x+=get_id(x);
    }
}
int Sum(int x){
    int ans=0;
    while(x>0){
        ans+=d[x];x-=get_id(x);
    }
    return ans;
}
int main(){
    while(scanf("%d",&n)==1){
            int aa,bb;
            intx();
        for(int i=1;i<=(n-1)*2;i++){
            scanf("%d%d",&aa,&bb);
            Next[i]=first[aa];
            first[aa]=i;
            vi[i]=bb;
            i++;
            Next[i]=first[bb];
            first[bb]=i;
            vi[i]=aa;
           // vec[aa].push_back(bb);vec[bb].push_back(aa);
        }
        dfs1(1,0);
        dfs2(1,1);
        memset(a,0,sizeof(a));
        for(int i=1;i<=n;i++) a[i]=1;
        memset(d,0,sizeof(d));
        get_d(n);
        int q;scanf("%d",&q);char s[5];
        for(int i=1;i<=q;i++){
            scanf("%s",s);
            if(s[0]=='Q'){
                scanf("%d",&aa);
                int t1=p[aa];int t2=p[aa]+num[aa]-1;
                printf("%d\n",Sum(t2)-Sum(t1-1));
            }
            else if(s[0]=='C'){
                scanf("%d",&aa);
                if(a[aa]==0){
                    a[aa]=1;update(p[aa],1);
                }
                else{
                    a[aa]=0;update(p[aa],-1);
                }
            }
        }
       // for(int i=1;i<=n;i++) vec[i].clear();
    }
    return 0;
}

 

传送门http://poj.org/problem?id=2481

题意:求一个区间是多少个区间真子区间(只允许一个端点相同)

解法:按照x升序排序后,x相同时按照y的降序排序;然后查询即可(可能存在完全相等的两个区间;要注意这个下标从0开始)

#include <iostream>
#include <algorithm>
#include <cstring>
#include <map>
#include <vector>
#include <cstdio>
#define N 100005
using namespace std;
typedef struct node{
    int x;int  y;int biao;
    friend bool operator<(node a,node b){
        if(a.x==b.x) return a.y>b.y;
        return  a.x<b.x;
    }
}node;
node a[N];
int d[N];int vis[N];
int get_id(int x){
    return x&(-x);
}
void update(int x){
    while(x<=N){
        d[x]++;x+=get_id(x);
    }
}
void Sum(int x,int biao){
    int ans=0;
    while(x>0){
        ans+=d[x];
        x-=get_id(x);
    }
    vis[biao]=ans;
}
int main(){
    int n;
    while(scanf("%d",&n)==1&&n){
        int xx,yy;node t;
        memset(vis,0,sizeof(vis));
        memset(d,0,sizeof(d));
        for(int i=1;i<=n;i++){
            scanf("%d%d",&a[i].x,&a[i].y);a[i].x++;a[i].y++;a[i].biao=i;
        }
       int u=0;
       sort(a+1,a+n+1);
       for(int i=1;i<=n;i++){
        if(i==1){
       Sum(a[i].y-1,a[i].biao);
       vis[a[i].biao]=u-vis[a[i].biao];
         }
         else{
            if(a[i].x==a[i-1].x&&a[i].y==a[i-1].y) vis[a[i].biao]=vis[a[i-1].biao];
            else{
                Sum(a[i].y-1,a[i].biao);    vis[a[i].biao]=u-vis[a[i].biao];
            }
         }
         u++;
         update(a[i].y);
       }
       for(int i=1;i<=n;i++){
        if(i==1) printf("%d",vis[i]);
        else printf(" %d",vis[i]);
       }
       printf("\n");
    }
    return 0;
}

 传送门http://poj.org/problem?id=2352

题意:给你每个星星的位置,求这个星星的坐标范围内有多少个星星(包括自己)

题解:按照x升序x相同时升序,查找统计即可;

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#define N 32005
#define M 15005
using namespace std;
typedef struct node{
    int x;int y;
    friend bool operator<(node a,node b){
        if(a.x==b.x) return a.y<b.y;
        return a.x<b.x;
    }
}node;
int n;
node a[M];
int d[N];int vis[N];
int get_id(int x){
    return x&(-x);
}
void update(int x){
    while(x<=N){
        d[x]++;
        x+=get_id(x);
   }
}
void Sum(int x){
    int ans=0;
    while(x>0){
        ans+=d[x];
        x-=get_id(x);
    }
    vis[ans]++;
}
int main(){
    while(scanf("%d",&n)==1){
    int xx;int yy;
    for(int i=1;i<=n;i++){
        scanf("%d%d",&xx,&yy);a[i].x=xx+1;a[i].y=yy+1;
      }
      memset(vis,0,sizeof(vis));
      memset(d,0,sizeof(d));
      sort(a+1,a+n+1);
      for(int i=1;i<=n;i++){
       update(a[i].y);
       Sum(a[i].y);
       }
       for(int i=1;i<=n;i++){
        printf("%d\n",vis[i]);
      }
    }
    return 0;
}

 最后一题:

传送门http://poj.org/problem?id=1195

题意:给一个正方形,初始都是0,1操作是一个点的修改,2查询左下角和右上角形成的矩阵的和

题解:一维树状数组拓展为二维树状数组 在x y方向彼此独立

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#define ll long long
using namespace std;
ll d[1030][1030];
int get_id(int x){
    return x&(-x);
}
void update(int x,int y,int vulue){
    for(int i=x;i<=1030;i+=get_id(i)){
        for(int j=y;j<=1030;j+=get_id(j)){
            d[i][j]+=vulue;
        }
    }
}
int sum(int x,int y){
    int ans=0;
    for(int i=x;i>0;i-=get_id(i)){
        for(int j=y;j>0;j-=get_id(j)){
            ans+=d[i][j];
        }
    }
    return ans;
}
int main(){
    int k;int n;
    while(scanf("%d %d",&k,&n)==2){
        if(k!=0) break;
        memset(d,0,sizeof(d));
        int t;
        int aa,bb,cc,x1,y1;
        while(scanf("%d",&t)==1&&t!=3){
            if(t==1){
                scanf("%d%d%d",&aa,&bb,&cc);
                update(aa+1,bb+1,cc);
            }
            else if(t==2){
                scanf("%d%d%d%d",&aa,&bb,&x1,&y1);
                aa++;bb++;x1++;y1++;
                printf("%d\n",sum(x1,y1)-sum(aa-1,y1)-sum(x1,bb-1)+sum(aa-1,bb-1));
            }
        }
    }
    return 0;
}

 

posted @ 2017-10-03 21:04  wang9897  阅读(188)  评论(0编辑  收藏  举报