2021-09-22am

9.22am的题解

今天上午考数据结构,下午综合,都不太难,随附题解一篇。


AM

T1 校门外的树

时间限制: 1 Sec 内存限制: 128 MB

题目描述

校门外有很多树,学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两种操作:
1)K=1,读入 l,r 表示在 l 到 r 之间种上一种树,每次操作种的树的种类都不同;
2)K=2,读入 l,r 表示询问 l 到 r 之间有多少种树。
注意:每个位置都可以重复种树。

输入

第一行 n,m 表示道路总长为 n,共有 m 个操作;
接下来 m 行为 m 个操作。

输出

对于每个 k=2 输出一个答案。

样例输入
5 4
1 1 3
2 2 5
1 2 4
2 3 5
样例输出
1
2
提示

【数据范围与提示】

对于 20% 的数据,1≤n,m≤100;

对于 60% 的数据,1≤n≤103 ,1≤m≤5×10 4 ;

对于 100% 的数据,1≤n,m≤5×10 4 ,保证 l,r>0。

本题与HH的项链颇有几分相似,如有需要者 https://www.luogu.com.cn/problem/P1972;

本题O(n^2)是过不去的,我们思考O(nlogn)的做法;

由于询问的是区间中树的种类数,所以问询区间中相同种类的树只有一个会起作用;

询问的右端点有多少个树的左端点,就代表了有可能有多少个树区间处于询问区间内;

而询问的左端点有多少个树的右端点,就代表了上述有多少个树区间实际并不处于询问区间内;(这一点应该好想);

于是答案即为ask(右端点的左括号个数)-ask(左端点的右括号个数);

我们发现修改和查询都可以在树状数组中执行,于是整个算法效率为O(mlogn)。

#include<bits/stdc++.h>
using namespace std;

int op,n,m,a,b;

int c1[50005],c2[50005];

void change(int x,bool flag)
{
	while(x<=n)
	{
		if(!flag)c1[x]++;
		else c2[x]++;
		x+=x&(-x);
	}
}

int ask(int x,bool flag)
{
	int ret=0;
	while(x)
	{
		if(!flag)ret+=c1[x];
		else ret+=c2[x]
		x-=x&(-x);
	}
	return ret;
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1; i<=m; i++)
	{
		scanf("%d%d%d",&op,&a,&b);
		if(op==1)
		{
			change(a,op-1);
			change(b,op);
		}
		else
		{
        printf("%d\n",ask(b,op-1)-ask(a-1,op));
		}
	}
}

T2 堆蛋糕

时间限制: 3 Sec 内存限制: 128 MB

其实小布是一个十分犀利的蛋糕师。他最喜欢的食物就是蛋糕。

一天,他自己做出了N个圆柱状的蛋糕,每个蛋糕都有一个底面圆的半径Ri。高度都是一样的。

小布在开始享用他的蛋糕大餐之前忽然觉得,圆柱状的蛋糕没有什么诱惑力。小布看到了别人结婚用的蛋糕都是很多很多层的,那样的蛋糕才比较给力。但是堆太多层的蛋糕比较困难,于是小布想要堆出许多三层的蛋糕,再开始自己的蛋糕大餐。

当然,作为蛋糕师,小布在堆蛋糕的时候不会对蛋糕的形状有任何破坏,而且,小布希望三层蛋糕的半径从上往下严格递增。这才是一个普通的好蛋糕。

但是小布在考虑一个十分重要的问题,最多可以堆出多少三层蛋糕呢?

输入

输入第一行仅包含一个整数N,表示蛋糕的数量。

接下来N个整数,表示每个蛋糕半径的大小Ri。

N<=3,000,000 Ri<=N

输出

输出一行仅包含一个整数,表示最多可以做成多少个蛋糕

样例输入
6
1 2 3 4 3 2
样例输出
2

本题贪心,需用优先队列优化。

策略:每次选择三个出现次数最大的半径,并都减去它们中的最小值,把不为0的入队,直到优先队列size小于3。

#include<bits/stdc++.h>
using namespace std;

int n,a[3000005],x,ans,minn;

int y[4];

priority_queue < int > q;

int main()
{
	scanf("%d",&n);
	for(int i=1; i<=n; i++)
	{
		scanf("%d",&x);
		a[x]++;//观察到r较小,可用类似桶排的想法保存信息
	}
	for(int i=1; i<=3000000; i++)
	{
		if(a[i])q.push(a[i]);//为啊a[i]=0的被筛掉
	}
	while(q.size()>=3)
	{
		y[1]=q.top(),q.pop();
		y[2]=q.top(),q.pop();
		y[3]=q.top(),q.pop();
		minn=min(y[1],min(y[2],y[3]));
		ans+=minn;
		for(int i=1; i<=3; i++)
		{
         if(y[i]-minn==0)
         {
         continue;	
		 }
		 else q.push(y[i]-minn);
		}
	}
	printf("%d",ans);
}

T3 清点人数

时间限制: 1 Sec 内存限制: 128 MB

题目描述

NK 中学组织同学们去五云山寨参加社会实践活动,按惯例要乘坐火车去。由于 NK 中学的学生很多,在火车开之前必须清点好人数。
初始时,火车上没有学生。当同学们开始上火车时,年级主任从第一节车厢出发走到最后一节车厢,每节车厢随时都有可能有同学上下。年级主任走到第 mm 节车厢时,他想知道前 mm 节车厢上一共有多少学生,但是他没有调头往回走的习惯。也就是说每次当他提问时,mm 总会比前一次大。

输入

第一行两个整数 n,k,表示火车共有 n 节车厢以及 k 个事件。
接下来有 k 行,按时间先后给出 k 个事件,每行开头都有一个字母 A,B 或 C。
如果字母为 A,接下来是一个数 m,表示年级主任现在在第 m 节车厢;
如果字母为 B,接下来是两个数 m,p,表示在第 m 节车厢有 p 名学生上车;
如果字母为 C,接下来是两个数 m,p,表示在第 m 节车厢有 p 名学生下车。
学生总人数不会超过 105 。

输出

对于每个 A ,输出一行,一个整数,表示年级主任的问题的答案。

样例输入

10 7
A 1
B 1 1
B 3 1
B 4 1
A 2
A 3
A 10
样例输出
0
1
2
3

提示

【数据范围与提示】

对于 30% 的数据,1≤n,k≤104 ,至少有 3000 个 A;

对于 100% 的数据,1≤n≤5×105 ,1≤k≤105 ,至少有 3×104 个 A。

本题A少,有可能n^2可过,有人过了,但笔者没有试过,故不加定论。

树状数组是可过的。

可以把本题看做树状数组的板。

#include<bits/stdc++.h>
using namespace std;

int n,k,m,p,mm,c[500005];

char op;

int ask(int x)
{
	int ret=0;
	while(x)
	{
		ret+=c[x];
		x-=x&(-x);
	}
	return ret;
}

void change(int x,int num)
{
	while(x<=n)
	{
		c[x]+=num;
		x+=x&(-x);
	}
}

int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=k;i++)
	{
		scanf("%s",&op);
	    if(op=='A')
	    {
	    	scanf("%d",&mm);
	    	printf("%d\n",ask(mm));
		}
		else if(op=='B')
		{
			scanf("%d%d",&m,&p);
			change(m,p);
		}
		else
		{
				scanf("%d%d",&m,&p);
			change(m,-p);
		}
	}
}
/*
10 8
A 1
B 3 1
B 1 4
B 4 1
A 2 
A 3
C 1 1
A 10

*/

T4 打鼹(yan)鼠

时间限制: 1 Sec 内存限制: 128 MB

题目描述

在这个“打鼹鼠”的游戏中,鼹鼠会不时地从洞中钻出来,不过不会从洞口钻进去(鼹鼠真胆大……)。洞口都在一个大小为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,鼹鼠数不会超过maxlongint。

输出

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

样例输入

4
1 2 2 5
2 0 0 2 3
3

样例输出

5

分析

img

先打三遍鼹鼹鼹,这是过题的关键(划掉

横看像什么二维树状数组,我旁边的老哥已经开始自创算法了…

其实不难。

我们把要求在线问题离线解决,倒序处理。M^2可解。

#include<bits/stdc++.h>
#define MAXN 10001
using namespace std;

int n,cnt;//now,xi,yi,xii,yii;
int ans[MAXN];
struct node
{
	int op;
	int x,y,k;
	int xI,yI,xII,yII;
}q[MAXN];
int main()
{
	scanf("%d",&n);
	int i=1;
	while(true)
	{
		int op;		scanf("%d",&op);
		if(op==2) scanf("%d%d%d%d",&q[i].xI,&q[i].yI,&q[i].xII,&q[i].yII),q[i].op=2;
		else if(op==1) scanf("%d%d%d",&q[i].x,&q[i].y,&q[i].k),q[i].op=1;
		else	break;
		++i;
	}
	for(int j=i-1;j>=1;j--)
	{
		if(q[j].op==2)
		{
			int nans=0;
			for(int r=1;r<j;r++)
			{
				if(q[r].op==2) continue;
				if(q[r].x <= q[j].xII && q[r].x >=q[j].xI )
				{
					if(q[r].y <= q[j].yII && q[r].y >=q[j].yI )
					{
						nans+=q[r].k;	
					}
				}
			}
			ans[++cnt]=nans;
		}
	}
	for(int j=cnt;j>=1;--j)
	{
		printf("%d\n",ans[j]);
	}
	return 0;
}
/*
4 
1 2 2 5
1 1 8 1
2 0 0 2 5

3
*/

T5 数列区间最大值

时间限制: 1 Sec 内存限制: 128 MB

题目描述

输入一串数字,给你 M 个询问,每次询问就给你两个数字 X, Y,要求你说出 X 到 Y 这段区间内的最大数。

输入

第一行两个整数 N,M 表示数字的个数和要询问的次数;
接下来一行为 N 个数;
接下来 M 行,每行都有两个整数 X,Y。

输出

输出共 M 行,每行输出一个数。

样例输入

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

样例输出

5
8

提示

【数据范围与提示】

对于全部数据,1≤N≤105 ,1≤M≤106 ,1≤X≤Y≤N。数字不超过 C/C++ 的 int 范围。

本题其实有不同的做法,但线段树会被卡,或许是因为其查询不是 O ( 1 ) O(1) O(1) 的(m较大)

故选用ST表,构建 O ( n l o g n ) O(nlogn) O(nlogn),查询 O ( 1 ) O(1) O(1).

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

int lg[100005],a[100005];

int f[100005][65];

int n,T;

int main()
{
	scanf("%d%d",&n,&T);
	for(int i=1; i<=n; i++)
	{
		scanf("%d",a+i);
		f[i][0]=a[i];
	}
	lg[1]=0;
	for(int j=2; j<=n; j++)
	{
		lg[j]=lg[j>>1]+1;
	}
	int t=lg[n]+1;
	for(int i=1; i<t; i++)
	{
		for(int j=1; j<=n-(1<<i)+1; j++)
		{
			f[j][i]=max(f[j][i-1],f[j+(1<<(i-1))][i-1]);
		}
	}
	for(int i=1; i<=T; i++)
	{
	int u,v,len;
     scanf("%d%d",&u,&v);
     len=v-u+1;
     t=lg[len];
     printf("%d\n",max(f[u][t],f[v-(1<<t)+1][t]));
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值