树状数组

入门篇:http://fqq11679.blog.hexun.com/21722866_d.html 

 

hdu1166 敌兵布阵 【单点更新,区间求和】

http://acm.hdu.edu.cn/showproblem.php?pid=1166 

最基本的模板题

#include<iostream>
#include<cstring>  
#include<string>
#include<cstdio>
using namespace std;

#define lowbit(x) ((x)&(-(x)))
int n;

void update(int *a,int x,int w)
{
	while(x<=n)
	{
		a[x]+=w;
		x+=lowbit(x);
	}
}

int sum(int *a,int x)
{
	int ans=0;
	while(x>0)
	{
		ans+=a[x];
		x-=lowbit(x);
	}
	return ans;
}
	
int main()
{
	int a[50001],b[50001];
	int T,t=1;
	scanf("%d",&T);
	while(T--)
	{
		int w;
		scanf("%d",&n);
		memset(a,0,sizeof(a));
		for(int i=1;i<=n;i++)
		{                    
			scanf("%d",&w);
		    update(a,i,w);
		}
		printf("Case %d:\n",t++);
		char s[10];
		int x,y;
		scanf("%s",&s);
		while(strcmp(s,"End")!=0)
		{                       
			if(strcmp(s,"Add")==0)
			{
				scanf("%d%d",&x,&y);
				update(a,x,y);
			}
			else if(strcmp(s,"Sub")==0)
			{
				scanf("%d%d",&x,&y);
				update(a,x,-y);
			}
			else if(strcmp(s,"Query")==0)
			{
				scanf("%d%d",&x,&y);
				int ans=sum(a,y)-sum(a,x-1);
				printf("%d\n",ans);
			}
			scanf("%s",&s);
		}
	}
	return 0;
}


 

 poj3928 Ping pong【单点更新,区间求和】

http://poj.org/problem?id=3928

题意:一条大街上从西到东依次住着N个乒乓球爱好者,他们经常举办比赛。每个人都有一个不同的技能值。每场比赛需要三个人,一个裁判加两个爱好者,不过比赛有一个要求就是裁判的技能值需要在两个爱好者的中间,并且居住的位置也要求在两个爱好者的中间,问总共能够举办多少场比赛。

思路:枚举每个人为裁判。ls[i]为裁判左边比他技能值小的人数,ll[i]为裁判左边比他技能值大的人数,rs[i]为裁判左边比他技能值小的人数,rl[i]为裁判右边比他技能值大的人数。则ls[i]*rl[i]+ll[i]*rs[i]即为所求结果。

 

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
using namespace std;

#define lowbit(x) ((x)&(-(x)))

const int INF=0x3f3f3f3f;

long long ans;
int T,n,maxx;
int a[100001],ll[20001],ls[20001],rl[20001],rs[20001],rank[20001];//ll:左大,ls:左小,rl:右大,rs:右小

void update(int *a,int x,int w)
{
    while(x<=maxx)
    {
        a[x]+=w;
        x+=lowbit(x);
    }
}

int sum(int *a,int x)
{
    int ans=0;
    while(x>0)
    {
        ans+=a[x];
        x-=lowbit(x);
    }
    return ans;
}

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        maxx=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&rank[i]);
            if(rank[i]>maxx)
                maxx=rank[i];
        }
        memset(a,0,sizeof(a));
        memset(ll,0,sizeof(ll));
        memset(ls,0,sizeof(ls));
        for(int i=1;i<=n;i++)
        {
            ls[i]=sum(a,rank[i]);
            ll[i]=i-1-ls[i];
            update(a,rank[i],1);
        }
        memset(a,0,sizeof(a));
        memset(rl,0,sizeof(rl));
        memset(rs,0,sizeof(rs));
        for(int i=n;i>0;i--)
        {
            rs[i]=sum(a,rank[i]);
            rl[i]=n-i-rs[i];
            update(a,rank[i],1);
        }
        ans=0;
        for(int i=1;i<=n;i++)
            ans+=(long long)ls[i]*rl[i]+(long long)ll[i]*rs[i];
        printf("%I64d\n",ans);
    }
    return 0;
}

 

poj2299 Ultra-QuickSort【单点更新,区间求和】

http://poj.org/problem?id=2299

题意:给你一个数列,每次只能交换相邻的两个数,问多少次交换可以将数列从小到大排序

思路:其实就是求逆序数个数。枚举每个数,之前比它大的数的个数加起来就是逆序数个数。由于数值范围是999999999,但个数只有500000,所以需要先离散化。

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
using namespace std;

#define lowbit(x) ((x)&(-(x)))

const int maxn=500001;

struct point
{
    int value,pos;
};

int n;
int c[maxn],f[maxn];
long long ans;
point a[maxn];

void update(int *a,int x,int w)
{
    while(x<=n)
    {
        a[x]+=w;
        x+=lowbit(x);
    }
}

int sum(int *a,int x)
{
    int ans=0;
    while(x>0)
    {
        ans+=a[x];
        x-=lowbit(x);
    }
    return ans;
}

void quicksort(int s,int t)
{
    int i,j,x,g;
    point k;
    i=s; j=t; g=(s+t)>>1; x=a[g].value;
    while(i<=j)
    {
        while(a[i].value<x) i++;
        while(a[j].value>x) j--;
        if(i<=j)
        {
            k=a[i]; a[i]=a[j]; a[j]=k;
            i++; j--;
        }
    }
    if(s<j) quicksort(s,j);
    if(i<t) quicksort(i,t);
}

int main()
{
    scanf("%d",&n);
    while(n!=0)
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i].value);
            a[i].pos=i;
        }
        quicksort(1,n);
        for(int i=1;i<=n;i++)   //离散化
            f[a[i].pos]=i;
        memset(c,0,sizeof(c));
        ans=0;
        for(int i=1;i<=n;i++)
        {
            ans+=(long long)i-1-sum(c,f[i]);
            update(c,f[i],1);
        }
        printf("%I64d\n",ans);
        scanf("%d",&n);
    }
    return 0;
}



 poj2481 Cows 【单点更新,区间求和】

http://poj.org/problem?id=2481

题意:有n头奶牛,每头奶牛覆盖一段区间[l,r]的牧草。若对于两只奶牛,l1<=l2,r1>=r2,r1-l1>r2-l2,则说奶牛1比奶牛2强。现在给你每头奶牛的牧草区域,问对于每头奶牛,有多少比它强的。

思路:离线。将奶牛的覆盖区域按l升序,r降序排列。那么对于每个r,之前只要出现一个比它大的ri,必定有li,ri覆盖的区域比l,r大(或者相等,特判),维护一个树状数组,对于每个r,看之前有多少个比它大的r即可。

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
using namespace std;

#define lowbit(x) ((x)&(-(x)))

struct point
{
    int l,r,id;
    bool operator < (const point & p) const
    {
        if(l==p.l) return r>p.r;
        else return l<p.l;
    }
};

const int maxn=100010;

int n,maxx;
int c[maxn],ans[maxn];
point a[maxn];

void update(int *a,int x,int w)
{
    while(x<=maxx)
    {
        a[x]+=w;
        x+=lowbit(x);
    }
}

int sum(int *a,int x)
{
    int ans=0;
    while(x>0)
    {
        ans+=a[x];
        x-=lowbit(x);
    }
    return ans;
}

int main()
{
    scanf("%d",&n);
    while(n!=0)
    {
        maxx=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&a[i].l,&a[i].r);
            a[i].id=i;
            if(a[i].r>maxx)
                maxx=a[i].r;
        }
        sort(a+1,a+n+1);
        memset(c,0,sizeof(c));
        memset(ans,0,sizeof(ans));
        for(int i=1;i<=n;i++)
        {
            if(i!=1 && a[i].l==a[i-1].l && a[i].r==a[i-1].r)
                ans[a[i].id]=ans[a[i-1].id];
            else
                ans[a[i].id]=i-1-sum(c,a[i].r-1);
            update(c,a[i].r,1);
        }
        for(int i=1;i<=n-1;i++)
            printf("%d ",ans[i]);
        printf("%d\n",ans[n]);
        scanf("%d",&n);
    }
    return 0;

}


 

hdu4638 Group【单点更新,区间查询】

http://acm.hdu.edu.cn/showproblem.php?pid=4638

题意:给你一串数,然后对于每次询问l和r,问你在[l,r]中有多少段连续的数

思路:树状数组,离线。每向后添加一个i位置的数a[i],先直接在这个位置+1,表示出现一个新组,然后看前面是否出现a[i]-1和a[i]+1,若出现a[i]-1,则在pos[a[i]-1]位置-1,a[i]+1同理。因为此时数段被合并起来了,a[i]就代表了它们的状态。将所有的询问按r从小到大排序。然后枚举i,树状数组维护的是第i个数之前那些-1,0,1的和。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cstdio>
using namespace std;

#define lowbit(x) ((x)&(-(x)))

const int maxn=100001;

struct point
{
    int l,r,id;
    bool operator < (const point & p) const
    {
        return r<p.r;
    }
};

int T,n,m;
int a[maxn],c[maxn],pos[maxn],ans[maxn];
bool mark[maxn];
point q[maxn];

void update(int *a,int x,int w)
{
    while(x<=n)
    {
        a[x]+=w;
        x+=lowbit(x);
    }
}

int sum(int *a,int x)
{
    int ans=0;
    while(x>0)
    {
        ans+=a[x];
        x-=lowbit(x);
    }
    return ans;
}

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            pos[a[i]]=i;
        }

        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&q[i].l,&q[i].r);
            q[i].id=i;
        }
        sort(q+1,q+m+1);
        memset(mark,0,sizeof(mark));
        memset(c,0,sizeof(c));
        int j=1;
        for(int i=1;i<=n;i++)
        {
            mark[a[i]]=1;
            update(c,pos[a[i]],1);
            if(a[i]-1>0 && mark[a[i]-1])
                update(c,pos[a[i]-1],-1);
            if(a[i]+1<=n && mark[a[i]+1])
                update(c,pos[a[i]+1],-1);
            while(q[j].r==i)
            {
                ans[q[j].id]=sum(c,q[j].r)-sum(c,q[j].l-1);
                j++;
            }
        }
        for(int i=1;i<=m;i++)
            printf("%d\n",ans[i]);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值