2023.3.19 模拟赛题解

银行取款

题意

  • 在现代文明社会中,大家在诸如银行办理业务、车站买票等活动时都很文明,没有插队的现象,本着"先来先服务"的规矩。
  • 初赛已经结束了,凡凡的爸爸打算上银行去取点钱,带着初赛考得很好的凡凡上街购物,凡凡的爸爸到银行时发现很多人在办理业务,凡凡的爸爸就自觉地在排队机上去了一个业务号码,并焦急的等待着银行柜台叫自己的号码......
  • 输入有若干行,每一行包含 I(表示等待办理业务)和顾客的序号;或者 是 O(表示办理完业务的人离开)。
  • 输出银行排队中出队顾客序列,若队列为空(没人等待),则输出 "None"。
  • 输入不超过 \(10^3\) 行。

题解

显然是队列板子题。

维护一个队列 q,这里偷懒用容器 std::queue

I 操作相当于 q.push(...)O 操作相当于输出 q.front(),然后 q.pop()。注意队列特判为空。

#include <queue>
#include <stdio.h>
class reque// 瞎封装一个吧,好看一点。
{
private:
	std::queue<int> q;
public:
	void insert(int val)
	{
		q.push(val);
		return ;
	}
	void erase()
	{
		if(q.empty())
			puts("None");
		else
		{
			printf("%d\n",q.front());
			q.pop();
		}
		return ;
	}
}q;
char opt[3];
int main()
{
	int id;
	while(~scanf("%s",opt))//单个字符转化成字符串,避免不可见字符问题。
	{
		if(opt[0]=='O')
		{
			q.erase();
		}else
		{
			scanf("%d",&id);
			q.insert(id);
		}
	}
	return 0;
}

聊天列表

题意

  • Smart 热衷于在社交网络上消磨时间。他在他最喜欢的网络中创建一个带有聊天列表的页面,这样当他向某个朋友发送消息时,他朋友的聊天内容就会上升到聊天列表的顶部。
  • 其他聊天的相对顺序没有改变。如果之前没有与这个朋友的聊天记录,那么一个新的聊天记录就会被插入到列表的顶部。
  • 假设聊天列表最初为空,给定 Smart 发消息的顺序,在处理完他的所有消息后,输出生成的聊天列表。
  • \(n\le 2\times 10^5\)

题解

首先将给定的聊天序列压入一个栈。

从栈顶开始扫,如果一个名字是第一次出现,则输出;反之忽略。

手模发现大概是正确的。

怎么实现呢,虽然给定 \(\forall str,|str| \le 10\),依然不想写字符串哈希,用 map 得了。

#include <map>
#include <stack>
#include <string>
#include <iostream>
std::map<std::string,bool>q;
std::stack<std::string>st;
std::string inp;
int main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	int n;
	std::cin>>n;
	while(n--)
	{
		std::cin>>inp;
		st.push(inp);
	}
	while(!st.empty())
	{
		if(!q.count(st.top()))
		{
			q[st.top()]=true;
			std::cout<<st.top()<<'\n';
		}
		st.pop();
	}
	return 0;
}

小麦高度

题意

  • Smart 和 Sarah 在每年的圣诞节都要种植小麦。众所周知,小麦的生长速度不同,经过一段时间后,种植的小麦会参差不齐。他们决定玩以下的游戏来解决这个问题:
    1、当轮到 Smart 的时候,他会选择最短的那个小麦,然后延长高度到第二短的小麦的高度。
    2、当轮到 Sarah 的时候,他会选择最长的那个小麦,然后剪短高度到第二长的小麦的高度。
  • 游戏如果能继续的话,小麦中必须有三种不同的高度,如果有人不能再继续游戏,那么他就失败了。
  • 给定所有小麦的高度,假设从 Smart 开始游戏,写一个程序决出游戏中谁是胜者,并输出游戏结束时最高的和最低的小麦的高度。

题解

直接模拟大概可以吧,用队列会快一些。

分讨一下最大值最小值出现次数的关系,判一下胜负完事了。

#include <stdio.h>
#define V 100000
int cnt[100010],q[100005];
int n,x,kinds,len,l,r,i;
int main()
{
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		scanf("%d",&x);
		if(!cnt[x])
			++kinds;
		++cnt[x];
	}
	for(i=1;i<=V;i++)
		if(cnt[i])
			q[++len]=i;
	l=1;r=len;
	if(kinds<3)
		printf("Sarah\n");
	else
		while(kinds>=3)
		{
			if(kinds>3)
			{
				if(cnt[q[l]]>cnt[q[r]])
				{
					cnt[q[l]]-=cnt[q[r]];
					cnt[q[l+1]]+=cnt[q[r]];
					cnt[q[r-1]]+=cnt[q[r]];
					--r;
					--kinds;
				}
				else if(cnt[q[l]]<cnt[q[r]])
				{
					cnt[q[r]]-=cnt[q[l]];
					cnt[q[l+1]]+=cnt[q[l]];
					cnt[q[r-1]]+=cnt[q[l]];
					++l;
					--kinds;
				}
				else if(kinds>4&&cnt[q[l]]==cnt[q[r]])
				{
					cnt[q[l+1]]+=cnt[q[l]];
					cnt[q[r-1]]+=cnt[q[l]];
					l++;
					r--;
					kinds-=2;
				}
				else if(kinds==4&&cnt[q[l]]==cnt[q[r]])
				{
					++l;
					--r;
					kinds-=2;
					printf("Sarah\n");
				}
			}
			else
			{
				if(cnt[q[l]]<cnt[q[r]])
				{
					++l;
					--kinds;
					printf("Smart\n");
				}else
				{
					--r;
					--kinds;
					printf("Sarah\n");
				}
			}
		}
	printf("%d %d\n",q[l],q[r]);
	return 0;
}

射击比赛

题意

  • 在奥运会射击比赛场上,假设所有的 \(n\) 名选手坐成一条直线。每个人都有自己的位置 \(x_i\) 和能力值 \(a_i\)
  • 如果一个人的左边 \(L\) 米内有人的能力值大于等于他的两倍,右边 \(L\) 米内也有人能力值大于等于他的两倍,那么这个人就会非常紧张,从而发挥失误。
  • Smart 现在想数一数有多少人会因为紧张而发挥失误。

题解

直接从头到尾,从尾到头两次单调队列即可。

实现好蠢。

\(pos\) 升序排一下

对于从头到尾的情况:

  • 首先将双端队列头部位置太远的全弹掉。
  • 然后将双端队列尾部权值太小的全弹掉。
  • 这样,双端队列的 \(pos\) 上升,\(v\) 下降。
  • 比对的话,直接查队列头的 \(v\) 就行了。

从尾到头同理。

其实这个显然线段树直接干也是可以的吧,又懒了。

#include <map>
#include <deque>
#include <stdio.h>
#include <algorithm>
class repair
{
public:
	int pos,s;
	repair():pos(0),s(0)
	{
	}
	inline void input()
	{
		scanf("%d %d",&pos,&s);
	}
	inline const bool operator<(const repair &other)const
	{
		return pos<other.pos;
	}
}v[50005];
std::deque<repair> q;
bool fr[50005],ba[50005];
int main()
{
	int n,i,L,ret=0;scanf("%d %d",&n,&L);
	for(i=1;i<=n;++i)
	{
		v[i].input();
	}
	std::sort(v+1,v+n+1);
	for(i=1;i<=n;++i)
	{
		while(!q.empty() && q.front().pos+L<v[i].pos)
			 q.pop_front();
		while(!q.empty() && q.back().s<v[i].s)
			q.pop_back();
		if(!q.empty() && q.front().s>=v[i].s*2)
			fr[i]=true;
		q.push_back(v[i]);
//		printf("%d %d %d %d\n",fr[i],q.size(),q.front().pos,q.front().s);
	}
//	puts("");
	q=std::deque<repair>();
	for(i=n;i>=1;--i)
	{
		while(!q.empty() && q.front().pos>v[i].pos+L)
			 q.pop_front();
		while(!q.empty() && q.back().s<v[i].s)
			q.pop_back();
		if(!q.empty() && q.front().s>=v[i].s*2)
			ba[i]=true;
		q.push_back(v[i]);
//		printf("%d %d %d %d\n",ba[i],q.size(),q.front().pos,q.front().s);
	}
	for(i=1;i<=n;++i)
		if(fr[i]&&ba[i])
			++ret;
	printf("%d",ret);
}

看戏

题意

吐槽一下这个 \(\LaTeX\)。懒得一个一个敲了,放图。

ppYwmSf.png
ppYwnl8.md.png

题解

\(t_i\) 差分数组为 \(d_i\),显然在 \(i\sim i+1\) 这两场中,移动的距离最多为 \(derta=d_{i+1}\times y\)

\(f_{i,j}\) 表示第 \(i\) 场,在 \(j\) 的最大价值,显然有转移:

  • \(f_{i,j}=\max\{f_{i-1,k}\}+\omega_{i,j} \mid dis(j,k)\le derta\)

发现这个玩意可以隔成两半:

  • \(x=\max\{f_{i-1,k}\}+\omega_{i,j} \mid j>k 且 j-k\le derta\)
  • \(y=\max\{f_{i-1,k}\}+\omega_{i,j} \mid j\le k 且 k-j\le derta\)
  • \(f_{i,j}=\max(x,y)\)

\(x,y\) 可以单调队列。

\(O(nm)\) 易求得 \(\omega_{i,j}\)

#include <deque>
#include <stdio.h>
#include <string.h>
const int V=1.5e5+5,inf=-2e9;
inline int max(int x,int y)
{
	return x>y?x:y;
}
inline int min(int x,int y)
{
	return x<y?x:y;
}

inline int abs(int x)
{
	return x>0?x:-x;
}
int der[V],val[V][305],t[V],q[V],pos[V],f[V][305];
int main()
{//freopen("e.out","w",stdout);freopen("e.in","r",stdin);
	int n,m,y,i,j,p,h,derta,l,r,ret=inf;
	scanf("%d %d %d",&n,&m,&y);
	for(i=1;i<=n;++i)
		for(j=1;j<=m;++j)
			f[i][j]=inf;
	for(i=1;i<=m;++i)
	{
		scanf("%d %d %d",&p,&h,t+i);
		for(j=1;j<=n;++j)
		{
			val[i][j]+=h-abs(j-p);
		}
		der[i]=t[i]-t[i-1];
	}
//	for(i=2;i<=n;++i)
//		f[0][i]=inf;
	for(i=1;i<=m;++i)
	{
		memset(q,0,sizeof q);
//		for(j=1;j<=n;++j)
//		{
//			printf("%d ",val[i][j]);
//		}puts("");
		derta=der[i]*y;//printf("%d\n",derta);
		q[l=r=1]=f[i-1][1];
//		printf("d:%d\n",q[l]);
		pos[1]=1;
		for(j=1;j<=n;++j)
		{
			f[i][j]=inf;
			while(l<=r && pos[l]<j-derta)
			{
				++l;
			}
			while(l<=r && q[r]<f[i-1][j])
			{
				--r;
			}
			if(l<=r)
				f[i][j]=val[i][j]+q[l];
//			printf("%d %d %d %d %d\n",l,r,j,val[i][j],q[l]);
			q[++r]=f[i-1][j];
			pos[r]=j;
		}
		q[l=r=1]=f[i-1][n];
		pos[1]=n;
//		puts("---");
		for(j=n;j>=1;--j)
		{
			while(l<=r && pos[l]>j+derta)
			{
				++l;
			}
			while(l<=r && q[r]<f[i-1][j])
			{
				--r;
			}
			if(l<=r && val[i][j]+q[l]>f[i][j])
				f[i][j]=max(val[i][j]+q[l],f[i][j]);
//			printf("%d %d %d %d %d\n",l,r,j,val[i][j],q[l]);
			q[++r]=f[i-1][j];
			pos[r]=j;
		}
		for(j=1;j<=n;++j)
		{
//			printf("%5d ",f[i][j]);
		}
//		puts("");
	}
	for(i=1;i<=n;++i)
		ret=max(ret,f[m][i]);
	printf("%d\n",ret);
}

但是你以为这就结束了?

注意数据范围,你的空间被卡了。所以你要压掉一维。

#include <deque>
#include <stdio.h>
#include <string.h>
const int V=1.5e5+5,inf=-2e9;
#define int long long
inline int max(int x,int y)
{
	return x>y?x:y;
}
inline int min(int x,int y)
{
	return x<y?x:y;
}

inline int abs(int x)
{
	return x>0?x:-x;
}
#undef int
int t[V],q[V],pos[V];
long long f[2][305],val[2][305];
int main()
{freopen("e.out","w",stdout);freopen("e.in","r",stdin);
	int n,m,y,i,j,p,h,derta,l,r;
	long long ret=inf;
	scanf("%d %d %d",&n,&m,&y);
	for(j=1;j<=m;++j)
		f[0][j]=f[1][j]=inf;
	bool flag=false;
	for(i=1;i<=m;++i)
	{
		scanf("%d %d %d",&p,&h,t+i);
		for(j=1;j<=n;++j)
		{
			val[flag][j]=0;
		}
		for(j=1;j<=n;++j)
		{
			val[flag][j]+=h-abs(j-p);
		}

//		for(j=1;j<=n;++j)
//		{
//			printf("%d ",val[flag][j]);
//		}puts("");
		derta=(t[i]-t[i-1])*y;//printf("%d\n",derta);
		q[l=r=1]=f[!flag][1];
//		printf("d:%d\n",q[l]);
		pos[1]=1;
		for(j=1;j<=n;++j)
		{
			f[flag][j]=inf;
			while(l<=r && pos[l]<j-derta)
			{
				++l;
			}
			while(l<=r && q[r]<f[!flag][j])
			{
				--r;
			}
			if(l<=r)
				f[flag][j]=val[flag][j]+q[l];
//			printf("%d %d %d %d %d\n",l,r,j,val[flag][j],q[l]);
			q[++r]=f[!flag][j];
			pos[r]=j;
		}
		q[l=r=1]=f[!flag][n];
		pos[1]=n;
//		puts("---");
		for(j=n;j>=1;--j)
		{
			while(l<=r && pos[l]>j+derta)
			{
				++l;
			}
			while(l<=r && q[r]<f[!flag][j])
			{
				--r;
			}
			if(l<=r && val[flag][j]+q[l]>f[flag][j])
				f[flag][j]=max(val[flag][j]+q[l],f[flag][j]);
//			printf("%d %d %d %d %d\n",l,r,j,val[flag][j],q[l]);
			q[++r]=f[!flag][j];
			pos[r]=j;
		}
		flag=!flag;
//		puts("");
	}
//	for(i=2;i<=n;++i)
//		f[0][i]=inf;
	for(i=1;i<=n;++i)
		ret=max(ret,f[!flag][i]);
	printf("%lld\n",ret);
}

无调试语句版:

#include <deque>
#include <stdio.h>
#include <string.h>
const int V=1.5e5+5;
long long inf=-1e17;
#define int long long
inline int max(int x,int y)
{
	return x>y?x:y;
}
inline int min(int x,int y)
{
	return x<y?x:y;
}

inline int abs(int x)
{
	return x>0?x:-x;
}
#undef int
int t[V],pos[V];
long long f[2][305],val[2][305],q[V];
int main()
{
	int n,m,y,i,j,p,h,derta,l,r;
	long long ret=inf;
	scanf("%d %d %d",&n,&m,&y);
	for(j=1;j<=m;++j)
		f[0][j]=f[1][j]=inf;
	bool flag=false;
	for(i=1;i<=m;++i)
	{
		scanf("%d %d %d",&p,&h,t+i);
		for(j=1;j<=n;++j)
		{
			val[flag][j]=0;
		}
		for(j=1;j<=n;++j)
		{
			val[flag][j]+=h-abs(j-p);
		}
		derta=(t[i]-t[i-1])*y;
		q[l=r=1]=f[!flag][1];
		pos[1]=1;
		for(j=1;j<=n;++j)
		{
			f[flag][j]=inf;
			while(l<=r && pos[l]<j-derta)
			{
				++l;
			}
			while(l<=r && q[r]<f[!flag][j])
			{
				--r;
			}
			if(l<=r)
				f[flag][j]=val[flag][j]+q[l];
			q[++r]=f[!flag][j];
			pos[r]=j;
		}
		q[l=r=1]=f[!flag][n];
		pos[1]=n;
		for(j=n;j>=1;--j)
		{
			while(l<=r && pos[l]>j+derta)
			{
				++l;
			}
			while(l<=r && q[r]<f[!flag][j])
			{
				--r;
			}
			if(l<=r && val[flag][j]+q[l]>f[flag][j])
				f[flag][j]=max(val[flag][j]+q[l],f[flag][j]);
			q[++r]=f[!flag][j];
			pos[r]=j;
		}
		flag=!flag;
	}
	for(i=1;i<=n;++i)
		ret=max(ret,f[!flag][i]);
	printf("%lld\n",ret);
}

总复杂度 \(O(nm)\),这里不能带 \(\log n\),所以 ST 表线段树等写不了了。

posted @ 2023-03-19 14:15  Syara  阅读(98)  评论(2编辑  收藏  举报