NOIP2023题解

news2024/11/26 9:57:35

T1:词典

有一个好猜的结论:对于一个字符串,若它当中的最小字符大于等于某其他字符串中的最大字符,那么这个字符串一定不可行。

证明也很简单,若最小字符大于最大字符,显然一定不可行。若最小字符等于最大字符,由于字符串长度相同,且字符串两两不同,所以次小的一定大于另一个次大的,依然不可行,由此得证。代码及其简单,就不贴了。

T2:三值逻辑

见到这个题第一眼就可以想到并查集了。对于 true 和 false,我们可以将它们视为正负关系,这样就极大简化操作了。初始化常量,令 T = − F , U = 0 T=-F,U=0 T=F,U=0,然后我们处理完所有赋值操作后,再处理答案。

考虑一个数 x x x,在并查集中,若 x x x 祖先为 U U U,那么答案自然加 1 1 1;若它的祖先有 − x -x x,说明是非关系起冲突了,那么 x x x 只能为 U U U。由此,我们处理的重点便是如何判断 x x x 是否存在这两个祖先。

x = T / F / U x=T/F/U x=T/F/U,可以直接判断;若之前访问过 − x -x x,那么也可以判断。然后考虑 x x x 大于或小于 0 0 0 的情况,注意处理数组越界的情况,就可以了。代码不长,但细节还是比较多的。

#include<bits/stdc++.h>
using namespace std;
#define rd read()
const int N=3e5+10,T=2e5+10,F=-T,U=0;
int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
	return x*f;
}
int c,t,n,m,fa[N],vis[N];
int find(int x){
	if(x==T||x==F) return x;
	if(x==U||vis[-x+n]) return U;
	if(vis[x+n]) return T;
	int res;
	if(x<0){
		if(x==-fa[-x]) return x;//为根节点
		vis[x+n]=1,res=find(-fa[-x]),vis[x+n]=0;
	}
	else{
		if(x==fa[x]) return x;
		vis[x+n]=1,res=fa[x]=find(fa[x]),vis[x+n]=0;
	}
	return res;
}
int main(){
	c=rd,t=rd;
	while(t--){
		n=rd,m=rd;memset(fa,0,sizeof(fa)),memset(vis,0,sizeof(is));
		for(int i=1;i<=n;i++) fa[i]=i;
		while(m--){
			char op;cin>>op;int x=rd,y;
			if(op=='T') fa[x]=T;
			else if(op=='F') fa[x]=F;
			else if(op=='U') fa[x]=U;
			else if(op=='+') y=rd,fa[x]=fa[y];
			else y=rd,fa[x]=-fa[y];
		}
		int ans=0;for(int i=1;i<=n;i++){
			//cout<<find(i)<<":::"<<endl;
			if(find(i)==U) ans++;
		}
		printf("%d\n",ans);
	}
	return 0;
}

T3:双序列拓展

感觉比 T4 难。

神仙思维题,依然是由特殊性质启发正解的题。

我们先看两个序列满足的条件,实际上就是对于任意位置 i i i,要么 f i > g i f_i>g_i fi>gi,要么 f i < g i f_i<g_i fi<gi,且这种大小关系只能满足一个。我们可以规定 f f f 恒小于 g g g,两种情况处理方法几乎是一样的。

先看 Subtask 1~7,可以用比较暴力的 O ( n 2 ) O(n^2) O(n2) 做法。 f i , g j f_i,g_j fi,gj 可以分别由 ( X i − 1 , Y j ) , ( X i , Y j − 1 ) , ( X i − 1 , Y j − 1 ) (X_{i-1},Y_j),(X_i,Y_{j-1}),(X_{i-1},Y_{j-1}) (Xi1,Yj),(Xi,Yj1),(Xi1,Yj1) 三种情况拓展过来。然后暴力枚举 i , j i,j i,j 即可,时间复杂度 O ( T n m ) O(Tnm) O(Tnm)

暴力的 dp 做法似乎没有什么优化的余地了,对正解也没什么启发,但我们将其抽象为一个网格图,发现了什么?
在这里插入图片描述
我们实际上就是从左上角的 ( 1 , 1 ) (1,1) (1,1),每次可以往右、往下、往右下移动,最终只要能到右下角的 ( n , m ) (n,m) (n,m) 即为成功。而设 ( x , y ) (x,y) (x,y) 分别表示 X , Y X,Y X,Y 的下标,我们能到这个格子,当且仅当 X x < Y y X_x<Y_y Xx<Yy

这样的转化看似影响不大,但我们再去看 Subtask 8~14,由于其特殊性质,发现只要 X m i n < Y m i n , X m a x < Y m a x X_{min}<Y_{min},X_{max}<Y_{max} Xmin<Ymin,Xmax<Ymax,那么这个网格就会像这样:
在这里插入图片描述
其中红格都是我们能到的地方,如果不理解的话一定要手动画图去体会一下,这是最关键的部分。

所以我们就可以通过找最大最小值位置,来判断是否合法并不断缩小范围了,最终只要 x = 1 x=1 x=1 y = 1 y=1 y=1,我们就可以构造出 f f f g g g 了。

但是若没有这个性质呢?实际上我们发现,改变的只有 X X X Y Y Y 的最值所在位置,所以网格图就会变为这种:
在这里插入图片描述
这个时候我们只要能从 ( 1 , 1 ) (1,1) (1,1) 到红格,并从红格到 ( n , m ) (n,m) (n,m) 即可。相比之前,我们需要多判断右下角。

至此,我们解决了这个问题。总结一下思路:预处理出 X X X Y Y Y 的前缀及后缀最值所在位置,然后两个 check 函数分别判断左上角和右下角的可行情况,不断缩小边界。

代码并不长,大多都是在预处理最值位置,关键部分就是两个 check 函数。

#include<bits/stdc++.h>
using namespace std;
#define rd read()
const int N=5e5+10;
int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
	return x*f;
}
int c,n,m,T,a[N],b[N],ta[N],tb[N],f[N],g[N];
struct node{int mnpos,mxpos;}prex[N],prey[N],sufx[N],sufy[N];
bool check1(int x,int y,int n,int m){
	if(x==1||y==1) return true;
	node A=prex[x-1],B=prey[y-1];
	if(f[A.mnpos]<g[B.mnpos]) return check1(A.mnpos,y,n,m);
	if(f[A.mxpos]<g[B.mxpos]) return check1(x,B.mxpos,n,m);
	return false;
}
bool check2(int x,int y,int n,int m){
	if(x==n||y==m) return true;
	node A=sufx[x+1],B=sufy[y+1];
	if(f[A.mnpos]<g[B.mnpos]) return check2(A.mnpos,y,n,m);
	if(f[A.mxpos]<g[B.mxpos]) return check2(x,B.mxpos,n,m);
	return false;
}
bool solve(int aa[],int bb[],int n,int m){
	if(aa[1]>=bb[1]) return false;
	memcpy(f,aa,sizeof(f)),memcpy(g,bb,sizeof(g));
	for(int i=1;i<=n;i++){
		if(i==1) prex[i]={1,1};
		else{
			prex[i].mnpos=prex[i-1].mnpos,prex[i].mxpos=prex[i-1].mxpos;
			if(f[i]<f[prex[i].mnpos]) prex[i].mnpos=i;
			if(f[i]>f[prex[i].mxpos]) prex[i].mxpos=i;
		}
	}
	for(int i=1;i<=m;i++){
		if(i==1) prey[i]={1,1};
		else{
			prey[i].mnpos=prey[i-1].mnpos,prey[i].mxpos=prey[i-1].mxpos;
			if(g[i]<g[prey[i].mnpos]) prey[i].mnpos=i;
			if(g[i]>g[prey[i].mxpos]) prey[i].mxpos=i;
		}
	}
	for(int i=n;i>=1;i--){
		if(i==n) sufx[i]={n,n};
		else{
			sufx[i].mnpos=sufx[i+1].mnpos,sufx[i].mxpos=sufx[i+1].mxpos;
			if(f[i]<f[sufx[i].mnpos]) sufx[i].mnpos=i;
			if(f[i]>f[sufx[i].mxpos]) sufx[i].mxpos=i;
		}
	}
	for(int i=m;i>=1;i--){
		if(i==m) sufy[i]={m,m};
		else{
			sufy[i].mnpos=sufy[i+1].mnpos,sufy[i].mxpos=sufy[i+1].mxpos;
			if(g[i]<g[sufy[i].mnpos]) sufy[i].mnpos=i;
			if(g[i]>g[sufy[i].mxpos]) sufy[i].mxpos=i;
		}
	}
	node A=prex[n],B=prey[m];
	if(f[A.mnpos]>=g[B.mnpos]||f[A.mxpos]>=g[B.mxpos]) return false;
	return check1(A.mnpos,B.mxpos,n,m)&&check2(A.mnpos,B.mxpos,n,m);
}
int main(){
	c=rd,n=rd,m=rd,T=rd;
	for(int i=1;i<=n;i++) a[i]=rd;
	for(int i=1;i<=m;i++) b[i]=rd;
	if(solve(a,b,n,m)||solve(b,a,m,n)) cout<<1;
	else cout<<0;
	while(T--){
		int kx=rd,ky=rd;
		memcpy(ta,a,sizeof(a)),memcpy(tb,b,sizeof(b));
		for(int i=1;i<=kx;i++){int x=rd,y=rd;ta[x]=y;}
		for(int i=1;i<=ky;i++){int x=rd,y=rd;tb[x]=y;}
		if(solve(ta,tb,n,m)||solve(tb,ta,m,n)) cout<<1;
		else cout<<0;
	}
	return 0;
}

T4:天天爱打卡

比较典的数据结构优化 dp。

先设状态, f i f_i fi 表示前 i i i 天,第 i i i 天必跑步,最终能量最大值。我们枚举上次从哪一天开始跑,转移方程不难得出:
f i = max ⁡ { max ⁡ t = 0 j − 1 f t − ( j − i ) × d + ∑ [ l p , r p ] ∈ [ j , i ] v p } f_i=\max \left \{ \max_{t=0}^{j-1}f_t-(j-i)\times d+\sum_{[l_p,r_p]\in [j,i]}v_p \right \} fi=max{maxt=0j1ft(ji)×d+[lp,rp][j,i]vp}
g i = max ⁡ j = 0 i f j g_i=\max_{j=0}^{i} f_j gi=maxj=0ifj,我们就有了 O ( n 2 ) O(n^2) O(n2) 的做法。发现这样的式子可以运用线段树优化,具体地:线段树叶子结点代表下标,维护相应位置最优值。对于 i → i + 1 i\to i+1 ii+1 [ 0 , i ] [0,i] [0,i] 中的所有位置值都会减 d d d,然后对于区间右端点等于 i + 1 i+1 i+1 的区间,设左端点为 l l l,则 [ 0 , l ] [0,l] [0,l] 中的值都会增加相应的 v v v。做完修改后,由于我们不能跑超过连续 k k k 天,所以我们查询 [ i − k + 1 , i + 1 ] [i-k+1,i+1] [ik+1,i+1] 中的最大值。此时最大值会改变,再令 i + 2 i+2 i+2 位置的值加上此时的答案。

此时时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),而 n n n 最大可达 1 0 9 10^9 109,考虑如何优化。发现 m m m 的数据范围很小,所以真正有用的位置并不多,只有一个区间的 l − 1 , r l-1,r l1,r 两个位置。将这些位置存下来进行离散化,再进行上述操作,这道题就做完了。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rd read()
const int N=2e5+10;
int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
	return x*f;
}
int c,T,n,m,k,d,lsh[N],tot,mx[N<<2],tag[N<<2];
struct node{int l,r,v;}a[N];
bool cmp(node a,node b){return a.r<b.r;}
void pushdown(int u){
	if(!tag[u]) return;
	tag[u<<1]+=tag[u],tag[u<<1|1]+=tag[u];
	mx[u<<1]+=tag[u],mx[u<<1|1]+=tag[u];
	tag[u]=0;
}
void pushup(int u){mx[u]=max(mx[u<<1],mx[u<<1|1]);}
void modify(int u,int l,int r,int ql,int qr,int v){
	if(ql<=l&&r<=qr){mx[u]+=v,tag[u]+=v;return;}
	pushdown(u);int mid=(l+r)>>1;
	if(ql<=mid) modify(u<<1,l,mid,ql,qr,v);
	if(qr>mid) modify(u<<1|1,mid+1,r,ql,qr,v);
	pushup(u);
}
int query(int u,int l,int r,int ql,int qr){
	if(ql<=l&&r<=qr) return mx[u];
	pushdown(u);int mid=(l+r)>>1,ans=0;
	if(ql<=mid) ans=max(ans,query(u<<1,l,mid,ql,qr));
	if(qr>mid) ans=max(ans,query(u<<1|1,mid+1,r,ql,qr));
	return ans;
}
signed main(){
	c=rd,T=rd;
	while(T--){
		n=rd,m=rd,k=rd,d=rd;tot=0;memset(mx,0,sizeof(mx)),memset(tag,0,sizeof(tag));
		for(int i=1;i<=m;i++){int x=rd,y=rd,v=rd;a[i]={x-y,x,v};}
		for(int i=1;i<=m;i++) lsh[++tot]=a[i].l,lsh[++tot]=a[i].r;
		sort(lsh+1,lsh+1+tot),tot=unique(lsh+1,lsh+1+tot)-lsh-1;
		for(int i=1;i<=m;i++) a[i].l=lower_bound(lsh+1,lsh+1+tot,a[i].l)-lsh,a[i].r=lower_bound(lsh+1,lsh+1+tot,a[i].r)-lsh;
		int ans=0,p=1;sort(a+1,a+1+m,cmp);
		for(int i=1;i<=tot;i++){
			modify(1,0,tot-1,0,i-1,-d*(lsh[i]-lsh[i-1]));
			while(p<=m&&a[p].r==i) modify(1,0,tot-1,0,a[p].l,a[p].v),p++;
			int t=lower_bound(lsh+1,lsh+1+tot,lsh[i]-k)-lsh;
			ans=max(ans,query(1,0,tot-1,t,i-1));
			if(i+1<tot) modify(1,0,tot-1,i+1,i+1,ans);
		}
		printf("%lld\n",ans);
	}
	return 0;
}

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

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

相关文章

常用Python数据分析开源库:Numpy、Pandas、Matplotlib、Seaborn、Sklearn介绍

文章目录 1. 常用Python数据分析开源库介绍1.1 Numpy1.2 Pandas1.3 Matplotlib1.4 Seaborn1.5 Sklearn 1. 常用Python数据分析开源库介绍 1.1 Numpy Numpy(Numerical Python)是Python数据分析必不可少的第三方库&#xff0c;Numpy的出现一定程度上解决了Python运算性能不佳的…

解决VScode(Visual Studio Code)中的乱码问题. 2024-10-13

系统环境: win10 64bit , vscode 1.94.2 1.乱码原因 默认使用utf-8编码,导致非utf-8内容乱码 2.解决乱码问题 1)打开设置 点击左下角的齿轮,然后再点击设置 2)启用编码自动检测 在设置搜索框中输入 编码 ,然后启用 自动猜测编码选项 3.乱码问题解决 重新打开文件后,乱码问…

清空redo导致oracle故障恢复---惜分飞

客户由于空间不足,使用> redo命令清空了oracle的redo文件 数据库挂掉之后,启动报错 Fri Oct 04 10:32:57 2024 alter database open Beginning crash recovery of 1 threads parallel recovery started with 31 processes Started redo scan Errors in file /home/oracle…

各种查询sql介绍

1. 关联查询&#xff08;JOIN&#xff09; 关联查询用于从多个表中检索数据。它基于两个或多个表之间的共同字段&#xff08;通常是主键和外键&#xff09;来组合数据。 内连接&#xff08;INNER JOIN&#xff09;&#xff1a; sql SELECT a.name, b.order_date FROM custome…

IO进程---day5

1、使用有名管道实现两个进程之间的相互通信 //管道文件 #include<myhead.h> int main(int argc, const char *argv[]) {//创建有名管道文件1if(mkfifo("./pipe1",0664)-1){perror("创建管道文件失败");return 0;}if(mkfifo("./pipe2",066…

upload-labs靶场Pass-03

upload-labs靶场Pass-03 分析源码 $is_upload false; $msg null; if (isset($_POST[submit])) {if (file_exists(UPLOAD_PATH)) {$deny_ext array(.asp,.aspx,.php,.jsp);$file_name trim($_FILES[upload_file][name]);$file_name deldot($file_name);//删除文件名末尾的…

Damn-Vulnerable-Drone:一款针对无人机安全研究与分析的靶机工具

关于Damn-Vulnerable-Drone Damn-Vulnerable-Drone是一款针对无人机安全研究与分析的靶机工具&#xff0c;广大研究人员可以利用该环境工具轻松学习、研究和分析针对无人机安全态势。 Damn Vulnerable Drone 基于流行的 ArduPilot/MAVLink 架构&#xff0c;其中故意留下了各种…

PDF 软件如何帮助您编辑、转换和保护文件

如何找到最好的 PDF 编辑器。 无论您是在为您的企业寻找更高效的 PDF 解决方案&#xff0c;还是尝试组织和编辑主文档&#xff0c;PDF 编辑器都可以在一个地方提供您需要的所有工具。市面上有很多 PDF 编辑器 — 在决定哪个最适合您时&#xff0c;请考虑这些因素。 1. 确定您的…

基于 Konva 实现Web PPT 编辑器(三)

完善公式 上一节我们简单讲述了公式的使用&#xff0c;并没有给出完整的样例&#xff0c;下面还是完善下相关步骤&#xff0c;我们是默认支持公式的编辑功能的哈&#xff0c;因此&#xff0c;我们只需要提供必要的符号即可&#xff1a; 符号所表达的含义是 mathlive 的command命…

从0开始深度学习(12)——多层感知机的逐步实现

依然以Fashion-MNIST图像分类数据集为例&#xff0c;手动实现多层感知机和激活函数的编写&#xff0c;大部分代码均在从0开始深度学习&#xff08;9&#xff09;——softmax回归的逐步实现中实现过 1 读取数据 import torch from torchvision import transforms import torchv…

JavaCove部署文档

1. 基础配置 1.1服务器&#xff1a; 2 核 2G 1.2. 一个域名 1.3. 项目地址&#xff1a; gitee:https://gitee.com/guo-_jun/JavaCove github:https://github.com/nansheng1212/JavaCove 2. CentOS 安装 Docker 官方网站上有各种环境下的 安装指南&#xff0c;这里主要介绍…

webpack自定义插件 ChangeScriptSrcPlugin

插件文件 class ChangeScriptSrcPlugin {apply(compiler) {const pluginName "ChangeScriptSrcPlugin";compiler.hooks.compilation.tap(pluginName, (compilation, callback) > {compilation.hooks.htmlWebpackPluginAlterAssetTags.tapAsync(pluginName,(html…

SpringCloudStream使用StreamBridge实现延时队列

利用RabbitMQ实现消息的延迟队列 一、安装RabbitMQ 1、安装rabbitmq 安装可以看https://blog.csdn.net/qq_38618691/article/details/118223851,进行安装。 2、安装插件 安装完毕后,exchange是不支持延迟类型的,需要手动安装插件,需要和安装的rabbitmq版本一致 https:…

动态规划:17.简单多状态 dp 问题_买卖股票的最佳时机III_C++

题目链接&#xff1a; 一、题目解析 题目&#xff1a;123. 买卖股票的最佳时机 III - 力扣&#xff08;LeetCode&#xff09; 解析&#xff1a; 拿示例1举例&#xff1a; 我们可以如图所示买入卖出股票&#xff0c;以求得最大利润&#xff0c;并且交易次数不超过2次 拿示…

基于SpringBoot设计模式之结构型设计模式·组合模式

文章目录 介绍开始架构图定义条目定义文件定义文件夹 测试样例 总结 介绍 能够使容器与内容具有一致性&#xff0c;创造出递归结构的模式就是 Composite 模式。Composite 在英文中是“混合物”“复合物”的意思。   以目录为例&#xff0c;在计算机中&#xff0c;某个目录下有…

在海外留学/工作,如何报考微软mos认证?

重点首先得强调的是&#xff0c;即使在海外也可以顺利地在国内获取微软MOS认证&#xff01; 01 微软mos认证简介 Microsoft Office Specialist 简称MOS。是微软公司和第三方国际认证机构、全球三大IT测验与教学中心之一的思递波/Certiport公司于1997年联合推出的&#xff0c;…

2009年国赛高教杯数学建模A题制动器试验台的控制方法分析解题全过程文档及程序

2009年国赛高教杯数学建模 A题 制动器试验台的控制方法分析 汽车的行车制动器&#xff08;以下简称制动器&#xff09;联接在车轮上&#xff0c;它的作用是在行驶时使车辆减速或者停止。制动器的设计是车辆设计中最重要的环节之一&#xff0c;直接影响着人身和车辆的安全。为了…

分享一个IDEA里面的Debug调试设置

1.问题来源 其实我们在这个IDEA里面的这个进行调试的时候&#xff0c;这个是只有步入&#xff0c;出去的选项的&#xff1b; 之前学习这个sort的底层源码的时候&#xff0c;进不去&#xff0c;我们是设置了一个取消java*什么的选项&#xff0c;然后使用这个step into就可以进…

计算机网络易混知识点

1.以太网采用曼彻斯特编码&#xff1b;以太网帧最短为64B&#xff0c;其中14个B首部(目的MAC-6B&#xff0c;源MAC-6B&#xff0c;类型-2B)4B尾部 2.OSI协议中&#xff0c;每一层为上一层提供服务&#xff0c;为下一层提供接口 3.帧序号的比特数表示的是发送窗口的大小&#…

java逻辑运算符 C语言结构体定义

1. public static void main(String[] args) {System.out.println(true&true);//&两者均为true才trueSystem.out.println(false|false);// | 两边都是false才是falseSystem.out.println(true^false);//^ 相同为false&#xff0c;不同为trueSystem.out.println(!false)…