牛客小白月赛90(A,B,C,D,E,F)

news2024/9/22 15:52:15

比赛链接

官方题解(视频)

这场偏思维,感觉好像没啥算法。E需要动态维护前 k k k 小,F是个离散化加dp,状态和递推方程比较 非常规,建议还是看一下涨涨姿势。


A 小A的文化节

思路:

签到

code:

#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=105;

int n,m,a[maxn];

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>a[i];
	int ans=0;
	for(int i=1,t;i<=m;i++){
		cin>>t;
		ans+=a[t];
	}
	cout<<ans;
	return 0;
}

B 小A的游戏

思路:

只有两种加分方式,一种是一个人加 3 3 3 分,另一个人加 0 0 0 分,一种是两人各加 1 1 1 分.。

发现如果没有平局的话,两人分数一定是 3 3 3 的倍数,加入平局后,两人分数的差值仍然是 3 3 3 的倍数。反过来,如果分数的差值是 3 3 3 的倍数的话,就可以凑出来某人赢了几场,平了几场,输了几场。换句话说,就是两人分数之差为 3 3 3 的倍数=有解。

code:

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

int T,a,b;

int main(){
	cin>>T;
	while(T--){
		cin>>a>>b;
		puts((abs(a-b)%3==0)?"Yes":"No");
	}
	return 0;
}

C 小A的数字

思路:

看半天以为是二进制位,结果是十进制位。。。

当然是尝试贪心地将每个数位都置为 0 0 0,如果本来就是 0 0 0 就置为 1 1 1。因为要求是正整数,所以结果为 0 0 0 的时候还需要特判。

code:

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

int T,n;

int main(){
	cin>>T;
	while(T--){
		cin>>n;
		int flag=n%10;
		stack<int> s;
		while(n){
			s.push((n%10)?0:1);
			n/=10;
		}
		while(!s.empty() && s.top()==0)s.pop();
		if(s.empty()){
			if(flag==1)s.push(2);
			else s.push(1);
		}
		
		while(!s.empty()){
			cout<<s.top();
			s.pop();
		}
		cout<<endl;
	}
	return 0;
}

D 小A的线段(easy version)

思路:

因为 m ≤ 10 m\le 10 m10 很小,所以直接暴力枚举选取线段的情况,然后逐一验证即可。

验证的时候可以用差分来优化。

code:

#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn=15;

int n,m;
int st[maxn],ed[maxn];

bool check(int sta){
	vector<int> a(n+5);
	for(int i=0;i<m;i++){
		if(sta>>i&1){
			a[st[i]]++;
			a[ed[i]+1]--;
		}
	}
	for(int i=1;i<=n;i++){
		a[i]+=a[i-1];
		if(a[i]<2)return false;
	}
	return true;
}

int main(){
	cin>>n>>m;
	for(int i=0;i<m;i++)cin>>st[i]>>ed[i];
	
	int ans=0;
	for(int i=0;i<(1<<m);i++)
		ans+=check(i);
	cout<<ans;
	return 0;
}

E 小A的任务

思路:

因为我们选了所有前 i i i A A A 任务,才有资格去前 i i i B B B 任务中选任务,一开始的想法是 d p dp dp。第 i i i 个任务我们可以只选一个 A A A 任务,或者同时选择一个 A A A 任务和相应的 B B B 任务,设 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示前 i i i 个任务选 j j j B B B 任务的最小时间,转移方程为 d p [ i ] [ j ] = m i n { d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − 1 ] + B i } + A i dp[i][j]=min\{dp[i-1][j],dp[i-1][j-1]+B_i\}+A_i dp[i][j]=min{dp[i1][j],dp[i1][j1]+Bi}+Ai。不过这样转移时间复杂度是 O ( n 2 ) O(n^2) O(n2) 的,因为它会同时处理出前 i i i 个任务中选择 0 ∼ i 0\sim i 0i B B B 任务的最小代价,因此时间复杂度降不下来,大部分数据我们根本用不到。

考虑其他做法,发现如果我们决定只做前 i i i A A A 任务后,我们做 k k k B B B 任务一定是做前 i i i 个中用时最小的 k k k 个任务。这么做的好处是我们可以使用一个堆来动态维护住前 i i i B B B 任务中最小的 k k k 个用时,这样我们跑一遍下来就得到了 i i i k ∼ n k\sim n kn 所有情况下 B B B 任务的选取情况,也就得到了最小的用时。验证一遍的时间复杂度是 O ( n ∗ l o g k ) O(n*logk) O(nlogk) 的,总时间复杂度应该是 O ( n ∗ q ∗ l o g k ) O(n*q*logk) O(nqlogk),运算次数约为 1 0 5 ∗ 100 ∗ l o g ≈ 2 ∗ 1 0 8 10^5*100*log\approx2*10^8 105100log2108,时限有 3 s 3s 3s ,可以通过。

code:

#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;

int n,q;
int a[maxn],b[maxn];

ll solve(int k){
	ll tot=0,ans;
	priority_queue<int> h;
	for(int i=1;i<=k;i++){
		tot+=a[i];
		tot+=b[i];
		h.push(b[i]);
	}
	ans=tot;
	for(int i=k+1;i<=n;i++){
		tot+=a[i];
		if(b[i]<h.top()){
			tot-=h.top();
			h.pop();
			tot+=b[i];
			h.push(b[i]);
			ans=min(ans,tot);
		}
	}
	return ans;
}

int main(){
	cin.tie(0)->sync_with_stdio(false);
	
	cin>>n>>q;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++)cin>>b[i];
	
	while(q--){
		int k;
		cin>>k;
		cout<<solve(k)<<endl;
	}
	return 0;
}

F 小A的线段(hard version)

思路:

不难发现其实区间里很多位置其实都是没啥用的。 1 0 5 10^5 105 个位置被 200 200 200 个线段分成了 400 400 400 多个段,段内的所有位置其实没啥用,我们其实可以直接把这些段看成一个点,一个位置。这样离散化一下, 1 0 5 10^5 105 个位置就变成了 400 400 400 多个位置。

因为每个位置只要覆盖两次及以上就行了,所以每个位置我们可以看作只有三种状态:覆盖了零次,覆盖了一次,覆盖两次以上,每条线段可以将若干位置的状态进行变化,因此考虑动态规划。

d p [ i ] [ j ] dp[i][j] dp[i][j] 表示前 i i i 个位置覆盖了两次及以上,前 j j j 个位置覆盖了一次的选取线段方法数,为了前面状态不会出现断层(比如前面先覆盖了两次,然后后面变成覆盖一次,然后又变成覆盖了两次),我们对所有线段按左端点进行排序,然后按顺序取更新 d p dp dp 值即可(因为我们考虑了左端点在前 i i i 个位置的线段,那么前面的位置就必须都是覆盖了两次,否则后面更新 d p dp dp 也不可能更新状态,这样就保证了我们不需要考虑前面出现断层的情况,因为一定更新不了答案)。

官方题解给出的状态转移方程如下:
在这里插入图片描述
我写的时候没看题解,直接分类讨论的,思路是一样的。转移方程如下:

假设我们现在考虑到了第 k k k 个线段,线段左右端点分别为 l , r l,r l,r,设 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示前 i i i 个位置覆盖了两次及以上,前 j j j 个位置覆盖了一次的选取线段方法数 ( l ≤ r , i ≤ j ) (l\le r,i\le j) (lr,ij)。于是有(箭头表示可以转移):

  1. 不选取该线段: d p [ k − 1 ] [ i ] [ j ] → d p [ k ] [ i ] [ j ] dp[k-1][i][j]\rightarrow dp[k][i][j] dp[k1][i][j]dp[k][i][j]
  2. 选取该线段:
    1. l − 1 ≤ i ≤ j ≤ r l-1\le i\le j\le r l1ijr 时, d p [ k − 1 ] [ i ] [ j ] → d p [ k ] [ j ] [ r ] dp[k-1][i][j]\rightarrow dp[k][j][r] dp[k1][i][j]dp[k][j][r]
    2. l − 1 ≤ i ≤ r < j l-1\le i\le r\lt j l1ir<j 时, d p [ k − 1 ] [ i ] [ j ] → d p [ k ] [ r ] [ j ] dp[k-1][i][j]\rightarrow dp[k][r][j] dp[k1][i][j]dp[k][r][j]
    3. r < i ≤ j r\lt i\le j r<ij 时, d p [ k − 1 ] [ i ] [ j ] → d p [ k ] [ i ] [ j ] dp[k-1][i][j]\rightarrow dp[k][i][j] dp[k1][i][j]dp[k][i][j]

上面第四条和第一条的实际意义是不一样的,前者表示不选取该线段的方法数,后者是选取的方法数,所以两者不影响,正常相加即可。

根据递推式,发现我们实际用的其实是 l − 1 l-1 l1,而不是 l l l,因为离散化后点与点之间相邻关系会被打破,比如 3 , 7 3,7 3,7 离散化后是 1 , 2 1,2 1,2,虽然离散化后 2 2 2 减一就等于 1 1 1,但这并不代表 7 7 7 减一就等于 3 3 3,所以我们离散化的时候直接对 l − 1 l-1 l1 离散化,而不是 l l l

另外就是 1 1 1 n n n 作为区间左右端点也要放入到离散化数组中去,左端点由于上面所有线段左端点都减一的影响,这里也要减 1 1 1,也就是说认为左端点是 0 0 0

code:

还能滚动数组优化,不过没写

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#define pii pair<int,int>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int maxm=205;
const ll mod=998244353;

int n,m;
vector<pii> itv;

vector<int> tmp;

int main(){
	cin>>n>>m;
	tmp.push_back(0);
	tmp.push_back(n);
	for(int i=1,l,r;i<=m;i++){
		cin>>l>>r;l--;
		tmp.push_back(l);
		tmp.push_back(r);
		itv.push_back(pii(l,r));
	}
	sort(tmp.begin(),tmp.end());
	tmp.erase(unique(tmp.begin(),tmp.end()),tmp.end());
	n=tmp.size();
	auto find=[&](int x)->int{return lower_bound(tmp.begin(),tmp.end(),x)-tmp.begin()+1;};
	for(auto& [l,r]:itv){
		l=find(l);
		r=find(r);
	}
	sort(itv.begin(),itv.end());
	
	vector<vector<vector<ll> > > dp(m+1,vector<vector<ll> >(n+1,vector<ll>(n+1,0)));
	dp[0][1][1]=1;
	for(int k=1;k<=m;k++){
		auto [l,r]=itv[k-1];
		for(int i=0;i<=n;i++)
			for(int j=i;j<=n;j++)
				dp[k][i][j]=dp[k-1][i][j];
		for(int i=0;i<=n;i++){
			for(int j=i;j<=n;j++){
				if(l<=i){
					if(i<=r){
						if(j<=r){
							dp[k][j][r]+=dp[k-1][i][j];
							dp[k][j][r]%=mod;
						}
						else {
							dp[k][r][j]+=dp[k-1][i][j];
							dp[k][r][j]%=mod;
						}
					}
					else {
						dp[k][i][j]+=dp[k-1][i][j];
						dp[k][i][j]%=mod;
					}
				}
			}
		}
		
	}
	cout<<dp[m][n][n]<<endl;
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1596693.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

都2024年了,还不知道怎么学习网络安全?来看看吧,很难找全的

前言 最近收到不少关注朋友的私信和留言&#xff0c;大多数都是零基础小友入门网络安全&#xff0c;需要相关资源学习。其实看过的铁粉都知道&#xff0c;之前的文里是有过推荐过的。新来的小友可能不太清楚&#xff0c;这里就系统地叙述一遍。 01.简单了解一下网络安全 说白…

CUDA编程---全局内存

CUDA内存模型概述 内存的访问和管理是所有编程语言的重要部分。在现代加速器中&#xff0c;内存管理对高性能计算有着很大的影响。因为多数工作负载被加载和存储数据的速度所限制&#xff0c;所以有大量低延迟、高带宽的内存对性能是十分有利的。 然而&#xff0c;大容量、高性…

如何做信创测试

信创测试是一种系统化的方法&#xff0c;旨在评估和验证创意和创新项目的潜力和可行性。进行信创测试可以帮助企业在投入大量资源之前&#xff0c;对创意进行客观、科学的评估&#xff0c;以减少失败的风险并最大化成功的可能性。以下是一般性的信创测试步骤&#xff1a; 确定…

Linux的学习之路:11、地址空间

摘要 本章主要是说一下地址空间&#xff0c;我也只是按照我的理解进行解释&#xff0c;可能说不清楚&#xff0c;欢迎指正 目录 摘要 一、空间布局图 二、代码测试一下 三、进程地址空间 四、测试代码 一、空间布局图 如下方图片可以看出地址空间有几种&#xff0c;这里…

成都欣丰洪泰文化传媒有限公司领航电商新纪元

在当今数字化飞速发展的时代&#xff0c;电商行业异军突起&#xff0c;成为推动经济增长的重要力量。在这股浪潮中&#xff0c;成都欣丰洪泰文化传媒有限公司以其专业的电商服务脱颖而出&#xff0c;成为业界的佼佼者。本文将带您一探这家公司的独特魅力和专业服务&#xff0c;…

MySQL查看表大小

1、使用SHOW TABLE STATUS命令&#xff1a; SHOW TABLE STATUS LIKE table_name;上述命令会返回包含表格信息的一行结果&#xff0c;其中有一个列为Data_length&#xff0c;表示表格占用的数据空间大小&#xff08;以字节为单位&#xff09;。 2、使用INFORMATION_SCHEMA库的…

Docker 学习笔记(五):梳理 Docker 镜像知识,附带 Commit 方式提交镜像副本,安装可视化面板 portainer

一、前言 记录时间 [2024-4-10] 前置文章&#xff1a; Docker学习笔记&#xff08;一&#xff09;&#xff1a;入门篇&#xff0c;Docker概述、基本组成等&#xff0c;对Docker有一个初步的认识 Docker学习笔记&#xff08;二&#xff09;&#xff1a;在Linux中部署Docker&…

吴恩达2022机器学习专项课程(一) 第二周课程实验:使用 scikit-learn 进行线性回归(Lab_05 Lab_06)

目标 使用scikit-learn实现线性回归(SGDRegressor和LinearRegression)。 1.什么是scikit-learn? 一个用于 Python 编程语言的开源机器学习库,用于实现各种机器学习算法。 2.特征缩放&#xff08;Z标准化&#xff09; 第一步先使用Z标准化处理训练样本&#xff0c;减少训练…

人员聚集监测识别摄像机

随着科技的不断发展&#xff0c;人员聚集监测识别摄像机已经成为了现代社会安全管理的重要工具。这种摄像机能够对人员聚集的情况进行实时监测和识别&#xff0c;帮助相关部门及时发现和处理潜在的安全风险。 人员聚集监测识别摄像机可以通过高清晰度的摄像头和先进的人脸识别技…

使用DockerCompose配置基于哨兵模式的redis主从架构集群

文章目录 一、注意事项&#xff08;坑点&#xff01;&#xff01;&#xff01;&#xff09;二、配置Redis主从架构集群第一步&#xff1a;创建目录文件结构第二步&#xff1a;编写DockerCompose配置文件第三步&#xff1a;编写redis.conf第四步&#xff1a;启动redis主从集群 三…

LeetCode 59.螺旋矩阵II

LeetCode 59.螺旋矩阵II 1、题目 力扣题目链接&#xff1a;59. 螺旋矩阵 II - 力扣&#xff08;LeetCode&#xff09; 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例 1&#xff1…

02_按键控制LED

按键控制LED 按键控制LED 按键控制LED while (1){//按键控制LEDif(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_5)GPIO_PIN_RESET)//读取PC5引脚状态&#xff0c;即检测按键是否按下{while(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_5)GPIO_PIN_RESET);//松手检测HAL_GPIO_WritePin(GPIOA,GPIO_PI…

使用IT-Tools+Cpolar在Windows搭建自己的IT工具箱并实现远程在线使用

文章目录 1. 使用Docker本地部署it-tools2. 本地访问it-tools3. 安装cpolar内网穿透4. 固定it-tools公网地址 本篇文章将介绍如何在Windows上使用Docker本地部署IT- Tools&#xff0c;并且同样可以结合cpolar实现公网访问。 在前一篇文章中我们讲解了如何在Linux中使用Docker搭…

Java-多线程-并发知识点02(面试/学习)

本文主要介绍了Java多线程中的线程池、Java中的锁、synchronized锁及相关问答等知识点 Java-多线程-并发知识点02 线程池如何创建线程池使用 ThreadPoolExecutor 类创建线程池使用 Executors 工厂类创建线程池 线程池有些什么参数&#xff1f;线程池的使用方法线程池常用的阻塞…

测试知识1

瀑布模型 瀑布模型是一个经典的软件开发生命周期模型&#xff0c;它将开发过程分为一系列顺序执行的阶段&#xff0c;每个阶段都有特定的任务和产出物。这些阶段通常包括需求分析、系统设计、实现、测试、部署和维护等。 在瀑布模型中&#xff0c;每个阶段的输出都作为下一个…

【VUE】Vue3自由拖拽标签

效果&#xff1a; 代码&#xff1a; <template> <div><div v-move class"box"><label class"move">拽我</label> </div> </div> </template> <script setup lang"ts">import { ref, …

如何通过Linux pciehp sysfs接口控制PCIe Slot电源状态?-2

NVME SSD电源状态判断 通过pciehp sysfs接口对NVMe SSD所在的PCIe插槽进行Power On/Off操作时&#xff0c;确实会间接影响到NVMe SSD本身的电源状态。因为NVMe SSD是作为PCIe设备连接到特定插槽上的&#xff0c;插槽电源状态的变化通常会直接影响到与其相连的设备。 当对PCIe…

【Css】table数据为空,以“-“形式展现

解决&#xff1a;class类名 它表示的是在一个名为class类名的元素内部&#xff0c;当该元素为空时&#xff0c;会在该元素的:before伪元素上应用一些样式。 这种写法通常用于在元素内容为空时&#xff0c;添加一些占位符或者提示文字

史上最全excel导入功能测试用例设计(以项目为例)

web系统关于excel的导入导出功能是很常见的&#xff0c;通常为了提高用户的工作效率&#xff0c;在维护系统中的一些数据的时候&#xff0c;批量导入往往比一个一个添加或者修改快很多。针对导入功能的测试&#xff0c;往往会有很多种情况&#xff0c;现在针对平时项目中遇到的…

五款靠谱平台,做地推网推赚钱的都不要错过

现在生活压力都很大&#xff0c;很多人的主业都已经满足不了生活的开支了&#xff0c;做副业增加收入成了大家的选择。对于没有特长的小伙伴来说&#xff0c;选择一个适合自己的副业非常重要。 在这种环境下&#xff0c;现在非常火爆的地推网推就成了很多人的选择。但对于不太…