Atcoder Beginner Contest 420 A-G题解
自我打ABC以来最简单的一场
第一次题解写完 A-G
8.27 补F 完结
A
代码
#include<bits/stdc++.h>
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
void read(int& x){
char c;
bool f=0;
while((c=getchar())<48) f|=(c==45);
x=c-48;
while((c=getchar())>47) x=(x<<3)+(x<<1)+c-48;
x=(f ? -x : x);
return;
}
int x,y;
int main(){
read(x),read(y);
printf("%d",(x-1+y)%12+1);
return 0;
}
//^o^
B
模拟
代码
#include<bits/stdc++.h>
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
const int maxn=1e6+5;
void read(int& x){
char c;
bool f=0;
while((c=getchar())<48) f|=(c==45);
x=c-48;
while((c=getchar())>47) x=(x<<3)+(x<<1)+c-48;
x=(f ? -x : x);
return;
}
int n,m;
int a[maxn];
string s[maxn];
int main(){
read(n),read(m);
for(int i=1;i<=n;i++){
cin>>s[i];
}
for(int i=0;i<m;i++){
int c0=0,c1=0;
for(int j=1;j<=n;j++){
if(s[j][i]=='0') ++c0;
else ++c1;
}
char c;
if(c0==0) c='1';
else if(c1==0) c='0';
else if(c0<c1) c='0';
else c='1';
for(int j=1;j<=n;j++){
if(s[j][i]==c) ++a[j];
}
}
int mx=0;
for(int i=1;i<=n;i++){
mx=max(mx,a[i]);
}
for(int i=1;i<=n;i++){
if(a[i]==mx) printf("%d ",i);
}
return 0;
}
//^o^
C
每次更新只改一个,所以重新计算一下这一个的价值即可
代码
#include<bits/stdc++.h>
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
const int maxn=1e6+5;
void read(int& x){
char c;
bool f=0;
while((c=getchar())<48) f|=(c==45);
x=c-48;
while((c=getchar())>47) x=(x<<3)+(x<<1)+c-48;
x=(f ? -x : x);
return;
}
void read(char& c){
do{
c=getchar();
}while(c==10||c==13||c==32);
}
int n,q;
int a[maxn],b[maxn];
int main(){
read(n),read(q);
for(int i=1;i<=n;i++) read(a[i]);
for(int i=1;i<=n;i++) read(b[i]);
LL ans=0;
for(int i=1;i<=n;i++) ans+=min(a[i],b[i]);
char tp;
int x,v;
while(q--){
read(tp),read(x),read(v);
ans-=min(a[x],b[x]);
if(tp=='A') a[x]=v;
else b[x]=v;
ans+=min(a[x],b[x]);
printf("%lld\n",ans);
}
return 0;
}
//^o^
D
多一维状态记录当前是否有踩到过奇数次开关过
然后跑BFS就好了
代码
#include<bits/stdc++.h>
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
const int maxn=505;
void read(int& x){
char c;
bool f=0;
while((c=getchar())<48) f|=(c==45);
x=c-48;
while((c=getchar())>47) x=(x<<3)+(x<<1)+c-48;
x=(f ? -x : x);
return;
}
void read(char& c){
do{
c=getchar();
}while(c==10||c==13||c==32);
}
struct node{
int x,y,s;
};
int n,m;
char mp[maxn][maxn];
bool vis[maxn][maxn][2];
int dis[maxn][maxn][2];
int sx,sy,ex,ey;
queue<int> q;
int wx[4]={0,0,-1,1},wy[4]={-1,1,0,0};
int main(){
read(n),read(m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
read(mp[i][j]);
if(mp[i][j]=='S') sx=i,sy=j;
if(mp[i][j]=='G') ex=i,ey=j;
}
}
queue<node> q;
q.push((node){sx,sy,0});
vis[sx][sy][0]=1;
dis[sx][sy][0]=0;
while(!q.empty()){
node u=q.front();
q.pop();
for(int i=0;i<4;i++){
node v=(node){u.x+wx[i],u.y+wy[i],u.s};
if(v.x>n||v.x<1||v.y>m||v.y<1) continue;
if(mp[v.x][v.y]=='?') v.s=!v.s;
if(vis[v.x][v.y][v.s]) continue;
if((v.s&&mp[v.x][v.y]=='o')) continue;
if(((!v.s)&&mp[v.x][v.y]=='x')) continue;
if(mp[v.x][v.y]=='#') continue;
vis[v.x][v.y][v.s]=1;
dis[v.x][v.y][v.s]=dis[u.x][u.y][u.s]+1;
q.push(v);
}
}
if((!vis[ex][ey][0])&&(!vis[ex][ey][1])){
printf("-1");
}
else if(!vis[ex][ey][0]){
printf("%d",dis[ex][ey][1]);
}
else if(!vis[ex][ey][1]){
printf("%d",dis[ex][ey][0]);
}
else{
printf("%d",min(dis[ex][ey][0],dis[ex][ey][1]));
}
return 0;
}
//^o^
E
很明显,这道题和图论没有任何关系,是个并查集板子
如果一个点所在的连通块里有黑点,那么它一定可以满足到达的条件
代码
#include<bits/stdc++.h>
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
const int maxn=2e5+5;
void read(int& x){
char c;
bool f=0;
while((c=getchar())<48) f|=(c==45);
x=c-48;
while((c=getchar())>47) x=(x<<3)+(x<<1)+c-48;
x=(f ? -x : x);
return;
}
int f[maxn];
void init(int n){
for(int i=1;i<=n;i++) f[i]=i;
}
int find_f(int x){
if(f[x]==x) return x;
else return f[x]=find_f(f[x]);
}
void merge(int x,int y){
x=find_f(x),y=find_f(y);
if(x==y) return;
f[x]=y;
}
int n,q;
int cnt[maxn];
bool p[maxn];
int main(){
read(n),read(q);
init(n);
int op,u,v;
while(q--){
read(op);
if(op==1){
read(u),read(v);
if(find_f(u)==find_f(v)) continue;
cnt[find_f(v)]+=cnt[find_f(u)];
merge(u,v);
}
else if(op==2){
read(u);
p[u]=!p[u];
if(p[u]) ++cnt[find_f(u)];
else --cnt[find_f(u)];
}
else{
read(u);
//cout<<find_f(u)<<' '<<cnt[find_f(u)]<<endl;
if(cnt[find_f(u)]) printf("Yes\n");
else printf("No\n");
}
}
return 0;
}
//^o^
F
思路参考这里
我们以每一行为基准线,求每个点能向上走的点数,记为其高度
然后对于每一个点,计算以其高度为最低点所能形成最大矩形

左边第一个小于它的和右边第一个小于等于它的(去重),可以通过单调栈求出
在这个矩形内,提前预处理出所有大小的矩形内包含合法子矩形的数量,注意这里要以最底部为起点开始计算
但这么做会重复计算不包含这一最低点的矩形,把这些矩形减掉即可
设其左边有宽为 \(x\) 的区间,右边有宽为 \(y\) 的区间,高度为 \(h\) 则其计数为
\(update\,on\,8.29\)
昨天晚上突然想到,来证明以下这个做法的正确性,不会重复计算
\(cnt[i][j]\) 之前说的不是很清楚,具体指的是以一条边作为底
长,宽分别不大于 \(i,j\) 切面积不大于 \(k\) 的矩形个数
注意是以一条边作为底的,其竖直状态下的转移方程如下:
对宽不作限制,状态转移方程如下:
假设底边固定在基准线上,这样就不会重复计算了
至于横向上的,这是我之前尝试 \(hack\) 的,如图

那么绿蓝重叠的方框内的矩形会不会重复计算呢?
通过上述的容斥后,我们发现对于一个需要计算的矩形
其计算在内的子矩形一定经过中轴线(即最低点所在的竖线,图中的红线)
所以横向上也是不会重复计算的
代码
#include<bits/stdc++.h>
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
void read(int& x){
char c;
bool f=0;
while((c=getchar())<48) f|=(c==45);
x=c-48;
while((c=getchar())>47) x=(x<<3)+(x<<1)+c-48;
x=(f ? -x : x);
return;
}
void read(char& c){
do{
c=getchar();
}while(c==10||c==13||c==32);
}
int n,m,k;
int main(){
read(n),read(m),read(k);
vector<vector<char>> mp(n+5,vector<char>(m+5,0));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
read(mp[i][j]);
}
}
vector<vector<int>> h(n+5,vector<int>(m+5,0));
vector<vector<LL>> cnt(n+5,vector<LL>(m+5,0));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(i*j<=k) ++cnt[i][j];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cnt[i][j]+=2*cnt[i][j-1];
if(j>1) cnt[i][j]-=cnt[i][j-2];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cnt[i][j]+=cnt[i-1][j];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(mp[i][j]=='.') h[i][j]=h[i-1][j]+1;
}
}
LL ans=0;
for(int i=1;i<=n;i++){
vector<int> l(m+5,0);
vector<int> r(m+5,m+1);
stack<int> st;
for(int j=1;j<=m;j++){
while((!st.empty())&&h[i][st.top()]>=h[i][j]) st.pop();
if(!st.empty()) l[j]=st.top();
st.push(j);
}
while(!st.empty()) st.pop();
for(int j=m;j>=1;j--){
while((!st.empty())&&h[i][j]<h[i][st.top()]) st.pop();
if(!st.empty()) r[j]=st.top();
st.push(j);
}
for(int j=1;j<=m;j++){
int x=j-l[j]-1,y=r[j]-j-1;
ans+=cnt[h[i][j]][x+y+1]-cnt[h[i][j]][x]-cnt[h[i][j]][y];
}
}
printf("%lld",ans);
return 0;
}
//^o^
G
这个就是初中数学强基的内容,而且是特别简单的那一种,放一下式子吧
设\(n^2+n+x=p^2\),其中 \(p\) 为整数
然后我们需要把所有 \(4x-1\) 的因数都求出来,求联立方程组
设 \(a \times b = 4x-1\),其中 \(a,b\) 为整数
代码
#include<bits/stdc++.h>
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
void read(LL& x){
char c;
bool f=0;
while((c=getchar())<48) f|=(c==45);
x=c-48;
while((c=getchar())>47) x=(x<<3)+(x<<1)+c-48;
x=(f ? -x : x);
return;
}
LL x;
vector<LL> ans;
int main(){
read(x);
x=4*x-1;
LL tp=(x<0 ? -x : x);
for(int i=1;1ll*i*i<=tp;i++){
if(x%i==0){
LL j=x/i;
if((i-j-2)%4==0) ans.push_back((i-j-2)/4);
if((j-i-2)%4==0) ans.push_back((j-i-2)/4);
}
}
sort(ans.begin(),ans.end());
ans.erase(unique(ans.begin(),ans.end()),ans.end());
printf("%d\n",(int)ans.size());
for(int i=0;i<(int)ans.size();i++){
printf("%lld ",ans[i]);
}
return 0;
}
//^o^

浙公网安备 33010602011771号