树状数组-题集

版权声明:QAQ https://blog.csdn.net/priesty_/article/details/82987186

简单:

1.poj2352 Stars

题目大意:

夜空中有N颗恒星(N≤100000),每颗恒星具有其坐标(x, y)(0≤x, y≤100000)。现在,天文学家要对这些恒星进行分类,分类的标准如下:对于任意一颗恒星S(x,y),如果存在k颗恒星,其x, y坐标均不大于S,则恒星S属于k类星。

现给出N颗恒星的坐标,要求统计出0~N-1类星的个数。

【输入格式】

输入文件第一行包含一个整数N,表示恒星总数。

接下来的N行每行两个整数表示一颗恒星的坐标。不存在两颗星拥有相同的坐标。

【输出格式】

    输出文件包含N行,每行包含一个整数,第i行表示第i-1类星的数量。

解析:

首先按x坐标从小到大排序,x相同则y坐标由小到大,然后从左到右扫描每个点,这样可以保证已经插入树状数组的点都在左侧或正下侧。

然后只需寻找有多少点位于当前点下方,很容易想到树状数组。处理完当前点后,将其按y坐标插入树状数组,即让a[y]加1;

最后注意x++,y++,避免循环卡0

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>
#include<deque>
#include<cmath>
#include<cctype>
#define LL long long
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1

using namespace std;

const int maxm=35000+5;
const int maxn=150000+5;

int n,m;
int ans[maxn];
int tree[maxn];

int read()
{
	char c=getchar();int x=0,f=1;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return x*f;
}

int lowbit(int x)
{
	return x & (-x);
}

void add(int x,int y)
{
	for(int i=x;i<maxn;i+=lowbit(i))
	{
		tree[i]+=y;
	}
}

int getsum(int x)
{
	int sum=0;
	for(int i=x;i>0;i-=lowbit(i))
	{
		sum+=tree[i];
	}
	return sum;
}

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		int x=read();
		int y=read();
		x++;
		ans[getsum(x)]++;
		add(x,1);
	}
	for(int i=0;i<n;i++) printf("%d\n",ans[i]);

	return 0;
}

2.Poj 2299 Ultra-QuickSort

题目大意:

给出长度为n的序列,每次只能交换相邻的两个元素,问至少要交换几次才使得该序列为递增序列。

 

解析:还是很好理解,就是求逆序对总数,也可以用归并来做

用树状数组时记得离散化一下(a[ ]数组),不然要炸;

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>
#include<deque>
#include<cmath>
#include<cctype>
#define LL long long
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1

using namespace std;

const int maxn=500000+5;

int n,m;
int a[maxn];
int tree[maxn];

struct node
{
	int mark;
	LL value;
}e[maxn];

int read()
{
	char c=getchar();int x=0,f=1;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return x*f;
}

bool cmp(node a,node b)
{
	return a.value<b.value;
}

int lowbit(int x)
{
	return x & (-x);
}

void add(int x,int y)
{
	for(int i=x;i<maxn;i+=lowbit(i))
	{
		tree[i]+=y;
	}
}

int getsum(int x)
{
	int sum=0;
	for(int i=x;i>0;i-=lowbit(i))
	{
		sum+=tree[i];
	}
	return sum;
}

int main()
{
	while(~scanf("%d",&n) && n)
	{
		memset(a,0,sizeof(a));
		memset(tree,0,sizeof(tree));
		for(int i=1;i<=n;i++)
		{
			scanf("%lld",&e[i].value);
			e[i].mark=i;
		}
		sort(e+1,e+n+1,cmp);
		for(int i=1;i<=n;i++)
		{
			a[e[i].mark]=i;
		}
		LL ans=0;
		for(int i=1;i<=n;i++)
		{
			add(a[i],1);
			ans+=i-getsum(a[i]);
		}
		printf("%lld\n",ans);
	}

	return 0;
}

3.HDU 1556 Color the ball

【题目大意】

N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的“小飞鸽"牌电动车从气球a开始到气球b依次给每个气球涂一次颜色。但是N次以后lele已经忘记了第I个气球已经涂过几次颜色了,你能帮他算出每个气球被涂过几次颜色吗?

【输入】

每个测试实例第一行为一个整数N,(N <= 100000).接下来的N行,每行包括2个整数a b(1 <= a <= b <= N)。
当N = 0,输入结束。

【输出】

每个测试实例输出一行,包括N个整数,第I个数代表第I个气球总共被涂色的次数。

解析:比较简单的模板题

很明显的,区间修改+单点查询

add(x,1),add(y+1,-1);

↑↑↑这个样子处理一下就成

最后避雷输出,结尾不能有多余空格

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>
#include<deque>
#include<cmath>
#include<cctype>
#define LL long long
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1

using namespace std;
//PE一次…… 

const int maxn=100000+10;

int n,m;
int tree[maxn];

int read()
{
	char c=getchar();int x=0,f=1;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return x*f;
}

int lowbit(int x)
{
	return x& (-x);
}

void add(int x,int value)
{
	for(int i=x;i<=n;i+=lowbit(i))
	{
		tree[i]+=value;
	}
}

int getsum(int x)
{
	int sum=0;
	for(int i=x;i>0;i-=lowbit(i))
	{
		sum+=tree[i];
	}
	return sum;
}

int main()
{
	while(~scanf("%d",&n) && n)
	{
		memset(tree,0,sizeof(tree));
		for(int i=1;i<=n;i++)
		{
			int x=read();
			int y=read();
			add(x,1);
			add(y+1,-1);
		}
		printf("%d",getsum(1));
		for(int i=2;i<=n;i++)printf(" %d",getsum(i));
		cout<<endl;
	}

	return 0;
}

4.Poj 3067 Japan

题意:日本岛东海岸与西海岸分别有N和M个城市,现在修高速公路连接东西海岸的城市,求交点个数。

解析:将每条路按x从小到达排序,若x相同,按y从小到大排序.

然后按排序后的公路用树状数组在线更新,求y的逆序数之和即为交点个数。

因为相交的情况必然是在在Li<Lj的情况下,Ri>Rj,至于证明,自己画个图就懂了

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>
#include<deque>
#include<cmath>
#include<cctype>
#define LL long long
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1

using namespace std;

const int maxn=10000000+4;

int n,m,k;
int tree[1000+10];

struct node
{
	int x,y;
}e[maxn];

int read()
{
	char c=getchar();int x=0,f=1;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return x*f;
}

bool cmp(node a,node b)
{
	if(a.x==b.x) return a.y<b.y;//!!!!
	return a.x<b.x;
}

int lowbit(int x)
{
	return x & (-x);
}

void add(int x,int y)
{
	for(int i=x;i<=m;i+=lowbit(i))
	{
		tree[i]+=y;
	}
}

LL getsum(int x)
{
	LL sum=0;
	for(int i=x;i>0;i-=lowbit(i))
	{
		sum+=tree[i];
	}
	return sum;
}

int main()
{
	int Case=0;
	int t=read();
	while(t--)
	{
		n=read(),m=read(),k=read();
		memset(tree,0,sizeof(tree));
		for(int i=0;i<k;i++)
		{
			e[i].x=read(),e[i].y=read();
		}
		sort(e,e+k,cmp);
		LL ans=0;
		for(int i=0;i<k;i++)
		{
			ans+=i-getsum(e[i].y);
			add(e[i].y,1);
		}
		
		printf("Test case %d: %lld\n",++Case,ans);
	}

	return 0;
}

中等:

1.Poj 2155 Matrix

题意:

给定一个n*n的矩阵,值全为0.每次更新的话是将(x1,y1)到(x2,y2)子矩阵里面的元素的0变成1,1变成0.

最后查询(x,y)节点的值。

解析:二维树状数组,结果对2取模就行

避雷输出,每一组数据输出之后还要输出空行orz

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>
#include<deque>
#include<cmath>
#include<cctype>
#define LL long long
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1

using namespace std;
//PE一次 

const int maxn=1000+10;

int n,m;
int tree[maxn][maxn];

int read()
{
	char c=getchar();int x=0,f=1;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return x*f;
}

int lowbit(int x)
{
	return x & (-x);
}

void add(int x,int y,int value)
{
	for(int i=x;i<=maxn;i+=lowbit(i))
	{
		for(int j=y;j<=maxn;j+=lowbit(j))
		{
			tree[i][j]+=value;
		}
	}
}

int getsum(int x,int y)
{
	int sum=0;
	for(int i=x;i>0;i-=lowbit(i))
	{
		for(int j=y;j>0;j-=lowbit(j))
		{
			sum+=tree[i][j];
		}
	}
	return sum;
}

int main()
{
	int t=read();
	while(t--)
	{
		memset(tree,0,sizeof(tree));
		char op[2];
		scanf("%d%d",&n,&m);
		for(int i=1;i<=m;i++)
		{
			scanf("%s",op);
			if(op[0]=='C')
			{
				int x1=read(),y1=read();
				int x2=read(),y2=read();
				x2++,y2++;
				add(x1,y1,1);
				add(x2,y2,1);
				add(x2,y1,-1);
				add(x1,y2,-1);
			}
			else
			{
				int x=read(),y=read();
				printf("%d\n",getsum(x,y)%2);
			}
		}
		cout<<endl;//高亮 
	}

	return 0;
}

2.Poj 1195 Mobile phones

题意:

定义三个操作:

开始的操作为 0 ,初始化 S * S大小的地图,值为0;

操作 1, 输入 X Y A, 将地图中坐标为 (X,Y)的值修改为A;

操作2, 输入 L B R T 查询 区间    (X,Y)   L<=X<=R , B<=Y<=T,  输出该矩形区间的和;

操作 3 结束程序;

解析:二维树状数组,点修改+区间查询

随便搞一下就行了

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>
#include<deque>
#include<cmath>
#include<cctype>
#define LL long long
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1

using namespace std;

const int maxn=1024+10;

int t,s,h;
LL tree[maxn][maxn];

int read()
{
	char c=getchar();int x=0,f=1;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return x*f;
}

int lowbit(int x)
{
	return x & (-x);
}

void add(int x,int y,int value)
{
	for(int i=x;i<=maxn;i+=lowbit(i))
	{
		for(int j=y;j<=maxn;j+=lowbit(j))
		{
			tree[i][j]+=value;
		}
	}
}

LL getsum(int x,int y)
{
	LL sum=0;
	for(int i=x;i>0;i-=lowbit(i))
	{
		for(int j=y;j>0;j-=lowbit(j))
		{
			sum+=tree[i][j];
		}
	}
	return sum;
}

int main()
{
	scanf("%d%d",&t,&s);
	memset(tree,0,sizeof(tree));
	while(~scanf("%d",&h) && h<3)
	{
		if(h==1)
		{
			int x=read(),y=read();
			int value=read();
			x++,y++;
			add(x,y,value);
		}
		else
		{
			int x=read(),y=read();
			int l=read(),r=read();
			l++,r++;
			LL ans=getsum(l,r)+getsum(x,y)-getsum(l,y)-getsum(x,r);
			printf("%lld\n",ans);
		}
	}

	return 0;
}

3.Poj 3321 Apple tree

 题目大意:

给出一颗苹果树,树的主干设为1,每一个分支设为一个数,一直到N,代表这颗苹果树。每个分支上面只能最多有一个苹果,也就是一个枝子上面不可能有两个苹果,另外注意一点,不要把苹果树想象成二叉树,苹果树每个节点可以分出很多叉,应该是多叉树。

输入是叉之间的关系:

1 2

1 3

就是主干上面两个叉分别是2 和3.

定义两种操作:

C   j  的意思是如果 j 这个枝子上面有苹果就摘下来,如果没有,那么就会长出新的一个;

Q  j  就是问 j 这个叉上面的苹果总数。

解析:

将题意提炼一下,就可以理解为对一段区间,进行点修改(摘下苹果or长出新的)+区间查询(询问苹果总数);

于是就往树状数组上靠

现在要考虑的问题是如何把一个树转化为一维数组

我们可以dfs:

对一棵树进行深搜,然后将深搜的顺序重新标上号,然后放到一个数组中,记下每一个枝子得起始点和终结点,然后就可以用树状数组了

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>
#include<deque>
#include<cmath>
#include<cctype>
#define LL long long
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1

using namespace std;

const int maxn=100000+10;

int n,m;
int tot=0;
char op[5];
int edges=0;
int head[maxn];
bool apple[maxn];
int tree[maxn],vis[maxn];
int begin[maxn],end[maxn];

struct node
{
	int from,to;
	int next;
}e[maxn*2];

int read()
{
	char c=getchar();int x=0,f=1;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return x*f;
}

void addedge(int u,int v)
{
	e[tot].from=u;
	e[tot].to=v;
	e[tot].next=head[u];
	head[u]=tot++;
}

void init()
{
	tot=0;
	memset(vis,0,sizeof(vis));
	memset(tree,0,sizeof(tree));
	memset(head,-1,sizeof(head));
	memset(apple,1,sizeof(apple));
}

int lowbit(int x)
{
	return x &(-x);
}

void dfs(int u)
{
	edges++;
	vis[u]=1;
	begin[u]=edges;
	for(int i=head[u];~i;i=e[i].next)
	{
		if(!vis[e[i].to])
		{
			dfs(e[i].to);
		}
	}
	end[u]=edges;
}

void add(int x,int value)
{
	for(int i=x;i<=n;i+=lowbit(i))
	{
		tree[i]+=value;
	}
}

int getsum(int x)
{
	int sum=0;
	for(int i=x;i>0;i-=lowbit(i))
	{
		sum+=tree[i];
	}
	return sum;
}

int main()
{
	init();
	scanf("%d",&n);
	for(int i=1;i<n;i++)
	{
		int u=read(),v=read();
		addedge(u,v);
		addedge(v,u);
	}
	edges=0;
	dfs(1);
	for(int i=1;i<=n;i++) add(i,1);
	m=read();
	for(int i=1;i<=m;i++)
	{
		scanf("%s",op);
		int u=read();
		if(op[0]=='C')
		{
			if(apple[u])
			{
				add(begin[u],-1);
				apple[u]=0;
			}
			else
			{
				add(begin[u],1);
				apple[u]=1;
			}
		}
		else 
		{
			int ans=getsum(end[u])-getsum(begin[u]-1);
			printf("%d\n",ans);
		}
	}
	

	return 0;
}

4.Poj 1990 MooFest

题意:

给出n头牛的叫声v和坐标x,两头牛如果能够交流则会花费max(v[i],v[j])*abs(x[i]-x[j]),问要使每头牛都能和其它牛交流需要花费多少

解析:

首先将这n头牛按照v值从小到大排序(后面说的排在谁的前面,都是基于这个排序)。这样,排在后面的牛和排在前面的牛讲话,两两之间所用的音量必定为后面的牛的v值。

然后,对于某头牛i来说,只要关心跟排在他前面的牛交流就好了。我们只需快速地求出排在他前面的牛和他之间距离的绝对值之和ans。

然后分两部分来计算,小于X0的,和大于X0的:

求出前面小于X0的坐标数s2,和那些小于X0的坐标之和s1,然后用total记录当前i个x坐标的所有和

距离即为:s2*X0 - s1 + total - s1 - x -(i - count)*X;

实际操作需要两个数组:treedistreecount,分别表示距离和个数;

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>
#include<deque>
#include<cmath>
#include<cctype>
#define LL long long
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1

using namespace std;

const int maxn=20000+100;

int n,m;
LL treedis[maxn];
LL treecount[maxn];

struct node
{
	int value,pos;
}e[maxn];

int read()
{
	char c=getchar();int x=0,f=1;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return x*f;
}

void init()
{
	memset(treedis,0,sizeof(treedis));
	memset(treecount,0,sizeof(treecount));
}

bool cmp(node a,node b)
{
	return a.value<b.value;
}

int lowbit(int x)
{
	return x & (-x);
}

void add(int x,LL value,LL *arr)
{
	for(int i=x;i<=maxn;i+=lowbit(i))
	{
		arr[i]+=value;
	}
}

LL getsum(int x,LL *arr)
{
	LL sum=0;
	for(int i=x;i>0;i-=lowbit(i))
	{
		sum+=arr[i];
	}
	return sum;
}

void solve()
{
	init();
	LL ans=0;
	LL totdis=0;
	sort(e,e+n,cmp);//!!!
	for(int i=0;i<n;i++)
	{
		LL count=getsum(e[i].pos,treecount);
		LL dis=getsum(e[i].pos,treedis);
		ans+=e[i].value*(count*e[i].pos-dis);//left
		ans+=e[i].value*(totdis-dis-(i-count)*e[i].pos);//right
		totdis+=e[i].pos;
		add(e[i].pos,1,treecount);
		add(e[i].pos,e[i].pos,treedis);
	}
	printf("%lld\n",ans);
}

int main()
{
	n=read();
	for(int i=0;i<n;i++)
	{
		e[i].value=read();
		e[i].pos=read();
	}
	solve();

	return 0;
}

5.Poj 3468 A Simple Problem with Integers

题意:

给出一组数组v[i],现有两种操作:

1)输出v[a]到v[b]之间的和;

2)让v[a]到v[b]之间的数全都加上c。

解析:区间修改+区间查询的模板题

emmm甩个隔壁友情链接,中间比较详细地讲了一下区间修改+查询的具体证明:一刀九十九级

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>
#include<deque>
#include<cmath>
#include<cctype>
#define LL long long
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1

using namespace std;

const int maxn=100000+10;

int n,m;
LL a[maxn];
LL sum[maxn];
LL c1[maxn],c2[maxn];

int read()
{
	char c=getchar();int x=0,f=1;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return x*f;
}

LL lowbit(LL x)
{
	return x & (-x);
}

void add(LL *arr,LL x,LL value)
{
	for(LL i=x;i<=n;i+=lowbit(i))
	{
		arr[i]+=value;
	}
}

LL getsum(LL x,LL *arr)
{
	LL sum=0;
	for(LL i=x;i>0;i-=lowbit(i))
	{
		sum+=arr[i];
	}
	return sum;
}

int main()
{
	char op[5];
	n=read(),m=read();
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		sum[i]=sum[i-1]+a[i];
	}
	LL ans=0;
	for(int i=1;i<=m;i++)
	{
		scanf("%s",op);
		if(op[0]=='Q')
		{
			LL s,t;
			scanf("%lld%lld",&s,&t);
			ans=sum[t]-sum[s-1];
			ans+=(t+1)*getsum(t,c1)-getsum(t,c2);
			ans-=(s*getsum(s-1,c1)-getsum(s-1,c2));
			printf("%lld\n",ans);
		}
		else
		{
			LL s,t,v;
			scanf("%lld%lld",&s,&t);
			scanf("%lld",&v);
			add(c1,s,v);
			add(c1,t+1,-v);
			add(c2,s,s*v);
			add(c2,t+1,-v*(t+1));
		}
	}

	return 0;
}

今天写了一天的树状数组,写完博客后发现也没几道emmm

比较困,写着写着就想摸鱼QAQ

以后再更新——

猜你喜欢

转载自blog.csdn.net/priesty_/article/details/82987186
今日推荐