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\)。懒得一个一个敲了,放图。
题解
设 \(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 表线段树等写不了了。