ABC420A-E题解
比赛链接
这次的A-E都比较简单。
A题
这里题目要你求当前为x月,过了y月会变成几月。这题x和y都不超过12,我们简单判断即可。
#include <bits/stdc++.h>
#define int long long
using namespace std;
int a,b;
signed main(){cin>>a>>b;if(a+b>12) cout<<a+b-12;else cout<<a+b;return 0;
}
B题
这里题目会给你n个字符串,每个是0/1,然后给这些人加分。
首先如果没有一个人选0或1,就给所有人加一分,否则就给少数群体加分。这里的n是奇数所以不可能两个群体一样多人。
这道题的数据范围使得我们可以直接模拟,这边维护一个数组用于记录分值,最后求有几个人的分数最多并输出编号即可。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=200;
int n,m,x,y,a[N],ma;
string s[N];
signed main(){cin>>n>>m;for(int i=1;i<=n;i++) cin>>s[i];for(int i=0;i<m;i++){x=y=0;for(int j=1;j<=n;j++){if(s[j][i]=='0') x++;else y++;}if(x==0||y==0){for(int j=1;j<=n;j++) a[j]++;}else if(x<y){for(int j=1;j<=n;j++){if(s[j][i]=='0') a[j]++;}}else{for(int j=1;j<=n;j++){if(s[j][i]=='1') a[j]++;} }}for(int i=1;i<=n;i++) ma=max(ma,a[i]);for(int i=1;i<=n;i++){if(a[i]==ma) cout<<i<<' ';}return 0;
}
代码比较长,但还是较容易理解的。
C题
这一道题会要求我们实现两个操作,第一个是把a数组中的一个元素改变,第二个是把b数组中的一个元素改变。每次操作完我们要把每个下标中a或b的元素的最小值累加起来并输出。
对于这道题我们肯定不能每次都算,所以我们可以维护哪个序列的元素更小,然后分别维护。最后我们改变这个值,同时输出改变的答案即可。这种避免整体操作,而是通过维护各种数组而进行局部操作的题型是很常见的。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
int n,q,a[N],b[N],x,y,mi[N],sl;
char op;
signed main(){cin>>n>>q;for(int i=1;i<=n;i++) cin>>a[i];for(int i=1;i<=n;i++) cin>>b[i];for(int i=1;i<=n;i++){if(a[i]<=b[i]) mi[i]=1, sl+=a[i];else mi[i]=2, sl+=b[i];}while(q--){cin>>op>>x>>y;if(mi[x]==1) sl-=a[x];else sl-=b[x];if(op=='A'){a[x]=y;if(a[x]<=b[x]) mi[x]=1, sl+=a[x];else mi[x]=2, sl+=b[x];}else{b[x]=y;if(a[x]<=b[x]) mi[x]=1, sl+=a[x];else mi[x]=2, sl+=b[x];}cout<<sl<<'\n';}return 0;
}
D题
D题是一道典型的进阶BFS题目。这道题加入了一个开关和两种门。第一种是原本开着的,一种是关闭的。每次我们经过开关,开着的门会关上,关着的门会打开。同时我们还有永久的障碍和空地。这道题与BFS模版最不同的地方就是这个开关。我们发现开关开多少次都只会有两个结果,所以我们维护一个vis数组,但我们不仅维护到达的位置,还要维护遇到几次开关。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=505;
int n,m,sx,sy,ex,ey,vis[N][N][2];
char a[N][N];
bool f;
int fx[10]={0,0,1,0,-1};
int fy[10]={0,1,0,-1,0};
struct node{int x,y,open,step;
};
void bfs(){queue<node>q;q.push({sx,sy,0,0}), vis[sx][sy][0]=1;while(q.size()){node t=q.front();q.pop();if(t.x==ex && t.y==ey){cout<<t.step;f=1;return;}int tx,ty;for(int i=1;i<=4;i++){tx=t.x+fx[i], ty=t.y+fy[i];if(tx<1||tx>n||ty<1||ty>m) continue;if(a[tx][ty]=='#'||(a[tx][ty]=='o'&&t.open)||(a[tx][ty]=='x'&&!t.open)) continue;if(a[tx][ty]=='?'){if(vis[tx][ty][t.open^1]) continue;vis[tx][ty][t.open^1]=1;q.push({tx,ty,t.open^1,t.step+1});}else{if(vis[tx][ty][t.open]) continue;vis[tx][ty][t.open]=1;q.push({tx,ty,t.open,t.step+1});}}}
}
signed main(){cin>>n>>m;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){cin>>a[i][j];if(a[i][j]=='S') sx=i,sy=j;if(a[i][j]=='G') ex=i,ey=j;}}bfs();if(!f) cout<<-1;return 0;
}
E题
这道题需要我们维护三个操作。第一个是在一个无向图中连接两个点,第二个是改变一个结点的颜色(初始都为白,改变一次变成黑,改变两次变回白,以此类推),第三个是查询一个结点能否到达一个黑色的结点。
对于这道题暴力枚举必死无疑。我们可以发现一个有趣的性质,即只要这个结点所处联通块有哪怕一个黑色结点,整个联通块的所有点在进行第三个操作时都会输出肯定答案。我们因此考虑维护一个并查集(无向图),这样我们可以轻松完成操作一并维护联通块。
然后我们维护一个序列保存每个结点的颜色,这样操作二也很简单。有了这两步的铺垫我们可以轻松的维护一个数组,保存每个联通块黑色结点的数量。这样当查询时只要联通块的黑色节点不为0就可以输出“Yes”。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
int n,q,f[N],num[N],op,x,y,a[N];
int fa(int x){if(f[x]==x) return x;else return f[x]=fa(f[x]);
}
signed main(){cin>>n>>q;for(int i=1;i<=n;i++) f[i]=i;while(q--){cin>>op>>x;if(op==1){cin>>y;int X=fa(x), Y=fa(y);if(X==Y) continue;f[Y]=X, num[X]+=num[Y];}else if(op==2){int X=fa(x);if(!a[x]) num[X]++, a[x]=1;else num[X]--, a[x]=0;}else{int X=fa(x);if(num[X]) cout<<"Yes"<<'\n';else cout<<"No"<<'\n';}}return 0;
}
这里记住进行操作一时不要忘记判断两个要连接的联通块会不会已经连一起了。
难度建议
红红橙黄黄