Atcoder Beginner Contest 241
在 Print 之前
这是 2.20 打的一场 VP 也是非常感人,第四题磕了一个多小时没磕出来,最后终于是过了6道题。
A - Digit Machine
此题也是非常的简单,总共 3 3 3 次操作,每次修改一下 a n s ans ans 即可。
A - Code
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=1e2+5;
ll a[N],ans;
int main() {
for(ll i=0;i<=9;i++) {
cin>>a[i];
}
ans=a[0];
ans=a[ans];
ans=a[ans];
cout<<ans;
return 0;
}
B - Pasta
此题更简单,用标记数组标记即可,但 a i a_{i} ai 和 b i b_{i} bi 值过大,所以建议用 m a p map map 。
B - Code
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=1e3+5;
ll n,m,a[N],b[N];
map<ll,ll> mp;
string s="Yes";
int main() {
cin>>n>>m;
for(ll i=1;i<=n;i++) {
cin>>a[i];
mp[a[i]]++;
}
for(ll j=1;j<=m;j++) {
cin>>b[j];
if(mp[b[j]]<=0) {
s="No";
break;
}
mp[b[j]]--;
}
cout<<s;
return 0;
}
C - Connect 6
题目说要找六个竖着、横着、左下斜着以及右下斜着的连续的 #
有两次修改机会。
这很明显是一个 模拟+dfs。
那么,我们可以思考这样一个问题:
我们需要往回搜吗(往左、上、右上、左上)?
不难发现,我们上面的情况是已经搜过的了,没必要再次枚举,所以直接往下搜即可。
结合代码可能会更好理解 。
C - Code
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=1e3+5;
ll n,cnt;
string s;
bool a[N][N],flag;
void dfs(ll x,ll y,ll num,ll k) {
bool f=0;
if(k==0&&a[x][y]==0) return ;
if(a[x][y]==0) {
k--;
a[x][y]=1;
f=1;
}
cnt++;
if(cnt>=6) return ;
if(num==1) {
if(x<=n&&y+1<=n&&x>=1&&y+1>=1) dfs(x,y+1,num,k);
}
if(num==2) {
if(x+1<=n&&y<=n&&x+1>=1&&y>=1) dfs(x+1,y,num,k);
}
if(num==3) {
if(x+1<=n&&y-1<=n&&x+1>=1&&y-1>=1) dfs(x+1,y-1,num,k);
}
if(num==4) {
if(x+1<=n&&y+1<=n&&x+1>=1&&y+1>=1) dfs(x+1,y+1,num,k);
}
if(f==1) {
a[x][y]=0;
}
}
int main() {
cin>>n;
for(ll i=1;i<=n;i++) {
cin>>s;
for(ll j=0;j<n;j++) {
a[i][j+1]=(s[j]=='#');
}
}
for(ll i=1;i<=n;i++) {
for(ll j=1;j<=n;j++) {
if(flag==1) break;
if(a[i][j]!=0) {
for(ll k=1;k<=4;k++) {
cnt=0;
dfs(i,j,k,2);
if(cnt>=6) {
flag=1;
break;
}
}
continue;
}
a[i][j]=1;
for(ll k=1;k<=4;k++) {
cnt=0;
dfs(i,j,k,1);
if(cnt>=6) {
flag=1;
break;
}
}
a[i][j]=0;
}
}
if(flag==1) {
cout<<"Yes";
}
else {
cout<<"No";
}
return 0;
}
D - Sequence Query
这题第一眼看时,就是个大模拟+二分,但如果
s
o
r
t
sort
sort 的话复杂度来到
O
(
Q
n
l
o
g
n
)
O(Qnlogn)
O(Qnlogn),包超时的 。
那么如何下手呢。
我们一看数据 :
1 ≤ Q ≤ 2 × 1 0 5 1\le Q\le2\times10^5 1≤Q≤2×105。
1 ≤ x ≤ 1 0 18 1\le x\le10^{18} 1≤x≤1018。
1 ≤ k ≤ 5 1\le k\le5 1≤k≤5。
只有个非常不合群的
k
≤
5
k\le5
k≤5,那么我们想办法优化
s
o
r
t
sort
sort,扩大查询复杂度不变
O
(
l
o
g
n
)
O(log n)
O(logn),不就行了。
这时只要上网一查 ,
m
u
l
t
i
s
e
t
multiset
multiset 好东西,自动排序,复杂度
O
(
1
)
O(1)
O(1),这就是为这题量身定做的,就是要思考一下查询:
当 n u m = 2 num=2 num=2 时,我们需要输出 A A A 中所有 ≤ x \le x ≤x 的元素中的第 k k k 大值。如果不存在输出
-1
。这只需要搜到第一个 > x >x >x 的数开始往后编译 k k k 次,如果 i t = a . b e g i n ( ) it=a.begin() it=a.begin() 时说明无解,否则输出 i t it it。
当 n u m = 3 num=3 num=3 时,我们需要输出 A A A 中所有 ≥ x \ge x ≥x 的元素中的第 k k k 小值。如果不存在输出
-1
。这只需要从第一个 ≥ x \ge x ≥x 的数开始遍历,总共遍历 k − 1 k-1 k−1 次,如果 i t = a . e n d ( ) it=a.end() it=a.end() 时说明无解,否则输出 i t it it。
D - Code
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=2e5+5;
ll T,f=0;
multiset<ll> a;
int main() {
cin>>T;
while(T--) {
ll num,x,k;
cin>>num>>x;
if(num==1) {
a.insert(x);
}
else {
cin>>k;
multiset<ll>::iterator cnt=a.lower_bound(x);
multiset<ll>::iterator ans=a.upper_bound(x);
if(num==2) {
ll op=0;
auto it=ans;
for(ll i=1;i<=k;i++) {
if(it==a.begin()) {
op=1;
break;
}
it--;
}
if(op==1) {
cout<<-1;
}
else {
cout<<*it;
}
}
else {
ll op=0;
auto it=cnt;
if(it==a.end()) op=1;
else {
for(ll i=1;i<k;i++) {
it++;
if(it==a.end()) {
op=1;
break;
}
}
}
if(op==1) {
cout<<-1;
}
else {
cout<<*it;
}
}
cout<<endl;
}
}
return 0;
}
E - Putting Candies
这是一个很明显的抽屉原理,它在查找一定次数后,就会陷入循环,这样我们只需要遍历 k k k m o d mod mod n n n 次就可以了。
E - Code
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=2e5+5;
ll a[N],sum[N],num[N],n,t,s,k;
int main() {
cin>>n>>k;
for(ll i=0;i<n;i++) {
cin>>a[i];
}
for(ll i=1;i<n;i++) {
num[i]=-1;
}
for(ll i=0;i<n;i++) {
sum[i+1]=sum[i]+a[sum[i]%n];
if(num[sum[i+1]%n]!=-1) {
s=num[sum[i+1]%n];
t=i+1;
break;
}
num[sum[i+1]%n]=i+1;
}
if(k<=s) cout<<sum[k];
else {
ll p=t-s;
ll x=sum[t]-sum[s],pos=k-s-1;
cout<<sum[s+pos%p+1]+pos/p*x;
}
return 0;
}
F - Skate
这是一道一眼的 b f s bfs bfs 但单单是搜索就很容易爆 T L E TLE TLE。
于是我们可以很容易 想到用
s
e
t
set
set 记录下每一行和每一列 分别有哪些障碍,这样向四个方向移动是就可以直接二分出该方向最近的障碍即可,可以使用 lower_bound
。
记录每一行和每一列分别有哪些障碍和从起点到各个点的距离等数据时,若是直接用数组存,显然直接 M L E MLE MLE 炸掉。我们可以用 m a p map map 代替数组存数据。
F - Code
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll h,w,n,sx,sy,ex,ey,ans=-1;
map<ll,set<ll> > x,y;
map<pair<ll,ll>,ll> d;
queue<pair<ll,ll> > q;
void bfs() {
q.push({sx,sy});
d[{sx,sy}]=0;
while(!q.empty()) {
pair<ll,ll> pl=q.front();
ll u=pl.first,v=pl.second;
q.pop();
if(u==ex&&v==ey) {
ans=d[{u,v}];
break;
}
auto it=x[u].lower_bound(v);
if(it!=x[u].begin()) {
if(d.find({u,*prev(it)+1})==d.end()) {
q.push({u,*prev(it)+1});
d[{u,*prev(it)+1}]=d[{u,v}]+1;
}
}
if(it!=x[u].end()) {
if(d.find({u,*it-1})==d.end()) {
q.push({u,*it-1});
d[{u,*it-1}]=d[{u,v}]+1;
}
}
it=y[v].lower_bound(u);
if(it!= y[v].begin()) {
if(d.find({*prev(it)+1,v})==d.end()) {
q.push({*prev(it)+1,v});
d[{*prev(it)+1,v}]=d[{u,v}]+1;
}
}
if(it!=y[v].end()) {
if(d.find({*it-1,v})==d.end()) {
q.push({*it-1,v});
d[{*it-1,v}]=d[{u,v}]+1;
}
}
}
}
int main() {
cin>>h>>w>>n>>sx>>sy>>ex>>ey;
for (ll i = 1; i <= n; i++) {
ll ul,vl;
cin>>ul>>vl;
x[ul].insert(vl);
y[vl].insert(ul);
}
bfs();
cout<<ans;
return 0;
}
后记
总体来说难度适中,不算太难但也不在舒适圈内,希望下次有进步 。