【数据结构】之树状数组与线段树的咋题题面加代码不解释

树状数组 1 :单点修改,区间查询

题目描述

输入一个数列A1,A2….An(1<=N<=100000),在数列上进行M(1<=M<=100000)次操作,操作有以下两种:

(1) 格式为C I X,其中C为字符“C”,I和X(1<=I<=N,|X|<=10000)都是整数,表示把把a[I]改为X

(2) 格式为Q L R,其中Q为字符“Q”,L和R表示询问区间为[ L ,R] (1<=L<=R<=N),表示询问A[L]+…+A[R]的值。

输入格式

第一行输入N(1<=N<=100000),表述数列的长度;

接下来N行,每行一个整数(绝对值不超过10000)依次输入每个数;

接下来输入一个整数M(1<=M<=100000),表示操作数量,接下来M行,每行为C I X或者Q L R。

输出格式

对于每个Q L R 的操作输出答案。

input

5
1
2
3
4
5
3
Q 2 3
C 3 9
Q 1 4

output

5
16

数据规模与约定

时间限制:1s
空间限制:256MB

#include<bits/stdc++.h>
using namespace std;
#define N 100010
int n,a[N],c[N],m,l,r;
char ch;
int lowbit(int x){
   
   return x&-x;}
void add(int x,int y){
   
   while(x<=n)c[x]+=y,x+=lowbit(x);}
int work(int x)
{
   
   
	int sum=0;
	while(x>0) sum+=c[x],x-=lowbit(x);
	return sum;
}
int main()
{
   
   
	cin>>n;
	for(int i=1;i<=n;i++){
   
   cin>>a[i];add(i,a[i]);}
	cin>>m;
	for(int i=1;i<=m;i++)
	{
   
   
		cin>>ch>>l>>r;
		if(ch=='C') add(l,r-a[l]),a[l]=r;
		else cout<<work(r)-work(l-1)<<endl;
	}
	return 0;
}

数星星

题目描述

天文学家经常要检查星星的地图,每个星星用平面上的一个点来表示,每个星星都有坐标。我们定义一个星星的“级别”为给定的星星中不高于它并且不在它右边的星星的数目。天文学家想知道每个星星的“级别”。

                        5
                      *
                    4
                  *
                1       2   3
              *       *   *

例如上图,5号星的“级别”是3(1,2,4这三个星星),2号星和4号星的“级别”为1。

给你一个地图,你的任务是算出每个星星的“级别”。

输入格式

输入的第一行是星星的数目N(1<=N<=60000),接下来的N行描述星星的坐标(每一行是用一个空格隔开的两个整数X,Y,0<=X,Y<=32000)。

星星的位置互不相同。星星的描述按照Y值递增的顺序列出,Y值相同的星星按照X值递增的顺序列出。

输出格式

输出包含N行,一行一个数。第i行是第i个星星的“级别”

input

5
1 1
5 1
7 1
3 3
5 5

output

0
1
2
1
3

数据规模与约定

时间限制:1s
空间限制:256MB

#include<bits/stdc++.h>
using namespace std;
#define N 100010
int n,c[N],a,b;
int lowbit(int x){
   
   return x&-x;}
void add(int x,int y){
   
   while(x<32100)c[x]+=y,x+=lowbit(x);}
int work(int x)
{
   
   
	int sum=0;
	while(x) sum+=c[x],x-=lowbit(x);
	return sum;
}
int main()
{
   
   
	cin>>n;
	for(int i=1;i<=n;i++){
   
   cin>>a>>b;a++,b++;cout<<work(a)<<endl;add(a,1);}
	return 0;
}

区间修改单点查询

题目描述

已知一个数列,你需要进行下面两种操作:

1.将某区间每一个数数加上x

2.求出某一个数的值

输入格式

第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。

第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

接下来M行每行包含2或4个整数,表示一个操作,具体如下:

操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k;

操作2: 格式:2 x 含义:输出第x个数的值。

输出格式

输出包含若干行整数,即为所有操作2的结果。

样例数据
input

5 5
1 5 4 2 3
1 2 4 2
2 3
1 1 5 -1
1 3 5 7
2 4

output

6
10

数据规模与约定

时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=8,M<=10

对于70%的数据:N<=10000,M<=10000

对于100%的数据:N<=500000,M<=500000

#include<bits/stdc++.h>
using namespace std;
#define N 1000100
int n,a[N],c[N],m,b,t,x,y,z;
int lowbit(int x){
   
   return x&-x;}
void add(int x,int y){
   
   while(x<=n)c[x]+=y,x+=lowbit(x);}
int work(int x)
{
   
   
	int sum=0;
	while(x>0) sum+=c[x],x-=lowbit(x);
	return sum;
}
int main()
{
   
   
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=m;i++)
	{
   
   
		scanf("%d",&t);
		if(t==1){
   
   scanf("%d%d%d",&x,&y,&z);add(x,z);add(y+1,-z);}
		else {
   
   scanf("%d",&x);printf("%d\n",a[x]+work(x));}
	}
	return 0;
}

打鼹鼠

题目描述

SuperBrother在机房里闲着没事干(再对比一下他的NOIP,真是讽刺啊…),于是便无聊地开始玩“打鼹鼠”…

在这个“打鼹鼠”的游戏中,鼹鼠会不时地从洞中钻出来,不过不会从洞口钻进去(鼹鼠真胆大……)。洞口都在一个大小为n(n<=1024)的正方形中。这个正方形在一个平面直角坐标系中,左下角为(0,0),右上角为(n-1,n-1)。洞口所在的位置都是整点,就是横纵坐标都为整数的点。而SuperBrother也不时地会想知道某一个范围的鼹鼠总数。这就是你的任务。

输入格式

每个输入文件有多行。

第一行,一个数n,表示鼹鼠的范围。

以后每一行开头都有一个数m,表示不同的操作:

m=1,那么后面跟着3个数x,y,k(0<=x,y<n),表示在点(x,y)处新出现了k只鼹鼠;

m=2,那么后面跟着4个数x1,y1,x2,y2(0<=x1<=x2<n,0<=y1<=y2<n),表示询问矩形(x1,y1)-(x2,y2)内的鼹鼠数量;

m=3,表示老师来了,不能玩了。保证这个数会在输入的最后一行。

询问数不会超过10000,鼹鼠数不会超过int。

输出格式

对于每个m=2,输出一行数,这行数只有一个数,即所询问的区域内鼹鼠的个数。

input

4
1 2 2 5
2 0 0 2 3
3

output

5

数据规模与约定

水题一道。 所有数据均为随机生成,包括样例……

时间限制:1s

空间限制:256MB

#include<bits/stdc++.h>
using namespace std;
int ans,n,m,x,y,xx,yy,k,a[2000][2000];
int lowbit(int x){
   
   return x&-x;}
void add(int x,int y,int k)
{
   
   
	for(int i=x;i<=n;i+=lowbit(i))
		for(int j=y;j<=n;j+=lowbit(j))
			a[i][j]+=k;
}
int work(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+=a[i][j];
	return sum;
}
int main()
{
   
   
	scanf("%d",&n);
	while(1)
	{
   
   
		scanf("%d",&m);
		if(m==3) break;
		else if(m==1)
		{
   
   
			scanf("%d%d%d",&x,&y,&k);
			x++,y++;
			add(x,y,k);
		}
		else
		{
   
   
			scanf("%d%d%d%d",&x,&y,&xx,&yy);
			ans=work(xx+1,yy+1)-work(xx+1,y)-work(x,yy+1)+work(x,y);
			printf("%d\n",ans);
		}
	}
	return 0;
} 

[USACO17JAN]Balanced Photo平衡的照片

题目描述

FJ正在安排他的N头奶牛站成一排来拍照。(1<=N<=100,000)序列中的第i头奶牛的高度是h[i],且序列中所有的奶牛的身高都不同。

就像他的所有牛的照片一样,FJ希望这张照片看上去尽可能好。他认为,如果存在i满足max(Ri,Li)>2*min(Li,Ri),第i头奶牛就是不平衡的。(Li和Ri分别代表第i头奶牛左右两边比她高的数量)。

也就是说,如果Li和Ri中的较大者严格来说是这两个数字中较小者的两倍以上,那么i就是不平衡的。

FJ不希望他有太多的奶牛不平衡,请你帮助FJ计算不平衡的奶牛数量。

输入格式

第一行一个整数N。

接下N行包括H[1]到H[n],每行一个非负整数(不大于1,000,000,000)。

输出格式

一行,一个整数,表示不平衡的奶牛数量。

样例数据
input

7
34
6
23
0
5
99
2

output

3

数据规模与约定

时间限制:1s
空间限制:256MB

#include<bits/stdc++.h>
using namespace std;
#define N 100010
int n,c[N],a[N],z[N],y[N],ans,len,b[N];
int lowbit(int x){
   
   return x&-x;}
int add(int x,int y){
   
   while(x<=n)c[x]+=y,x+=lowbit(x);}
int work(int x)
{
   
   
	int sum=0;
	while(x>0)sum+=c[x],x-=lowbit(x);
	return sum;
}
int main()
{
   
   
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {
   
   scanf("%d",&a[i]);b[i]=a[i];} 
	sort(b+1,b+n+1);
	len=unique(b+1,b+n+1)-(b+1);
	for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+len+1,a[i])-b;
	for(int i=1;i<=n;i++) {
   
   z[i]=i-1-work(a[i]);add(a[i],1);}
	memset(c,0,sizeof(c));
	for(int i=n;i>=1;i--) {
   
   y[i]=n-i-work(a[i]);add(a[i],1);}
	for(int i=1;i<=n;i++)
		if(2*min(z[i],y[i])<max(z[i],y[i])) ans++;
	cout<<ans;
}

人品累加和

题目描述

人品是必不可少的,人品还是守恒的。每个人的人品都是不同的,并且有正的(选择题可以用骰子全过),也有负的。

jzyz有n (1 <= n <= 100,000)个学生,第i个人的人品值是A_i(-10,000 <= A_i <= 10,000).

jzyz决定重新规划寝室,要让每个寝室的人品和不小于0.寝室的人品和是这个寝室里所有人的人品和。

因为名单是按照学籍号给出的,所以,现在你不能调整学生的顺序,只能按照输入的顺序把学生依次分进各个寝室,当然,为了增加难度,每个寝室的人数可以不同,我们甚至可以把n个学生放在同一个寝室。我们的口号,人品比宿舍重要。

比如现在有4个人,人品分别是2, 3, -3, 1。我们可以按照下面分寝室:

(2 3 -3 1)
(2 3 -3) (1)
(2) (3 -3 1)
(2) (3 -3) (1)
这样保证每个寝室的人品和不小于0.

现在的问题是,如果按照这种原则,有多少种分寝室的方法。当然,方法可能很多,你只需要输出方案数对 1,000,000,009 取余数即可。

输入格式

第一行一个整数n

接下来n行,表示每个学生的人品A_i.

输出格式

一个整数,如题所求的那个余数

input

4
2
3
-3
1

output

4

数据规模与约定

40% n<=1000.
100% 数据如题目描述。
时间限制:1s
空间限制:256MB

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define MOD 1000000009
#define N 100010
int len,c[N],b[N],n,f[N],a[N],ans;
int lowbit(int x){
   
   return x&-x;}
void add(int x,int y){
   
   while(x<=n+1){
   
   f[x]+=y;f[x]%=MOD;x+=lowbit(x);}}
int ask(int x)
{
   
   
	int c=0;
	while(x){
   
   c=(c+f[x])%MOD;x-=lowbit(x);}
	return c%MOD;
}
signed main()
{
   
   
	scanf("%lld",&n);
	for(int i=1;i<=n;i++){
   
   scanf("%lld",&a[i]);c[i]=c[i-1]+a[i];b[i]=c[i];}
	sort(b+1,b+2+n);
	len=unique(b+1,b+2+n)-(b+1);
	for(int i=1;i<=1+n;i++){
   
   c[i]=lower_bound(b+1,b+n+2,c[i])-b;}
	add(c[n+1],1);
	for(int i=1;i<=n;i++){
   
   ans=ask(c[i])%MOD;add(c[i],ans);}
	printf("%lld",ans);
	return 0;
}

楼兰图腾

Description

在完成了分配任务之后,西部314来到了楼兰古城的西部。相传很久以前这片土地上(比楼兰古城还早)生活着两个部落,一个部落崇拜尖刀(‘V’),一个部落崇拜铁锹(‘∧’),他们分别用V和∧的形状来代表各自部落的图腾。

西部314在楼兰古城的下面发现了一幅巨大的壁画,壁画上被标记出了N个点,经测量发现这N个点的水平位置和竖直位置是两两不同的。西部314认为这幅壁画所包含的信息与这N个点的相对位置有关,因此不妨设坐标分别为(1,y1),(2,y2),…,(n,yn),其中y1~yn是1到n的一个排列。

西部314打算研究这幅壁画中包含着多少个图腾,其中V图腾的定义如下(注意:图腾的形式只和这三个纵坐标的相对大小排列顺序有关)1 <= i < j < k <= n且yi > yj,yj < yk;

而崇拜∧的部落的图腾被定义为1 <= i < j < k <= n且yi < yj , yj > yk;

西部314想知道,这n个点中两个部落图腾的数目。因此,你需要编写一个程序来求出V的个数和∧的个数。

= = = 精简版说明:

平面上有 N(N≤10^5 ) 个点,每个点的横、纵坐标的范围都是 1~N,任意两个点的横、纵坐标都不相同。

若三个点 (x1,y1),(x2,y2),(x3,y3)(x_1,y_1),(x_2,y_2),(x_3,y_3 )(x1,y1),(x2,y2),(x3,y3) 满足 x1<x2<x3,y1>y2x_1 < x_2 < x_3, y_1 > y_2x1<x2<x3,y1>y2 并且 y3>y2y_3 > y_2y3>y2,则称这三个点构成"v"字图腾。

若三个点 (x1,y1),(x2,y2),(x3,y3)(x_1,y_1),(x_2,y_2),(x_3,y_3 )(x1,y1),(x2,y2),(x3,y3) 满足 x1<x2<x3,y1<y2x_1 < x_2 < x_3, y_1 < y_2x1<x2<x3,y1<y2 并且 y3<y2y_3 < y_2y3<y2,则称这三个点构成"^"字图腾。

求平面上"v"和"^"字图腾的个数。
####Input Format

第一行一个数n

第二行是n个数,分别代表y1,y2……yny_1,y_2……y_ny1y2……yn

Output Format

两个数

中间用空格隔开

依次为V的个数和∧的个数

Sample Input
5
1 5 3 2 4

Sample Output
3 4
Hint

数据范围约定

10%的数据 n<=600

40%的数据 n<=5000

100%的数据 n<=200000,答案不超过int64
####Limitation

各个测试点1s

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define N 200010
int n,ans,a[N],r[N],l[N],c[N];
int lowbit(int x) {
   
   return x&-x;}
int ask(int x)
{
   
   
	int sum=0;
	while(x>0) sum+=c[x],x-=lowbit(x);
	return sum;
}
void add(int x,int y) {
   
   while(x<=n) c[x]+=y,x+=lowbit(x);}
signed main()
{
   
   
	scanf("%lld",&n);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	memset(c,0,sizeof(c));
	for(int i=n;i;i--) {
   
   r[i]=ask(a[i]-1);add(a[i],1);}
	memset(c,0,sizeof(c));
	for(int i=1;i<=n;i++) {
   
   l[i]=ask(a[i]-1);add(a[i],1);}
	for(int i=1;i<=n;i++) ans+=(i-1-l[i])*(n-i-r[i]);  
	printf("%lld ",ans);
	ans=0;
	for(int i=1;i<=n;i++) ans+=(l[i])*r[i];
	printf("%lld",ans);
} 

A Simple Problem with Integers

题目描述

对于数列 A1A_1A1, A2A_2A2, … , ANA_NAN. 你要进行2个操作:将一个区间的数同加上某个数,输出一段区间的和。

输入格式

第一行2个整数表示数列长度和操作次数. 1 ≤ N,Q ≤ 100000.

第二行为数列 A1, A2, … , AN. -1000000000 ≤ Ai ≤ 1000000000.

接下来的Q行操作:

“C a b c” 表示将 Aa, Aa+1, … , Ab.加上c. -10000 ≤ c ≤ 10000.

“Q a b” 输出区间[a,b]的和。

输出格式

输出所有询问的答案,每行1个。

样例数据
input

10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

output

4
55
9
15

数据规模与约定

时间限制:5s
空间限制:128MB

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define N 100010
int n,m,sum[N],a[N],c[2][N],ans,l,r,d;
int lowbit(int x){
   
   return x&-x;}
void add(int x,int y,int z){
   
   for(;y<=n;y+=lowbit(y))c[x][y]+=z;}
int ask(int x,int y){
   
   int ans=0;for(;y;y-=lowbit(y))ans+=c[x][y];return ans;}
signed main()
{
   
   
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++){
   
   scanf("%lld",&a[i]);sum[i]=sum[i-1]+a[i];}
	while(m--)
	{
   
   char op[2];
		scanf("%s%lld%lld",op,&l,&r);
		if(op[0]=='C')
		{
   
   
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值