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

思路参考这里

我们以每一行为基准线,求每个点能向上走的点数,记为其高度

然后对于每一个点,计算以其高度为最低点所能形成最大矩形

image

左边第一个小于它的和右边第一个小于等于它的(去重),可以通过单调栈求出

在这个矩形内,提前预处理出所有大小的矩形内包含合法子矩形的数量,注意这里要以最底部为起点开始计算

但这么做会重复计算不包含这一最低点的矩形,把这些矩形减掉即可

设其左边有宽为 \(x\) 的区间,右边有宽为 \(y\) 的区间,高度为 \(h\) 则其计数为

\[cnt[h][x+y+1]-cnt[h][x]-cnt[h][y] \]

\(update\,on\,8.29\)

昨天晚上突然想到,来证明以下这个做法的正确性,不会重复计算

\(cnt[i][j]\) 之前说的不是很清楚,具体指的是以一条边作为底

长,宽分别不大于 \(i,j\) 切面积不大于 \(k\) 的矩形个数

注意是以一条边作为底的,其竖直状态下的转移方程如下:

\[cnt[i][j]+=cnt[i-1][j] \]

对宽不作限制,状态转移方程如下:

\[cnt[i][j]+=2*cnt[i][j-1]-cnt[i][j-2] \]

假设底边固定在基准线上,这样就不会重复计算了

至于横向上的,这是我之前尝试 \(hack\) 的,如图

image

那么绿蓝重叠的方框内的矩形会不会重复计算呢?

通过上述的容斥后,我们发现对于一个需要计算的矩形

其计算在内的子矩形一定经过中轴线(即最低点所在的竖线,图中的红线)

所以横向上也是不会重复计算的

代码
#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\) 为整数

\[(n+\frac{1}{2})^2+x-\frac{1}{4}=p^2 \]

\[p^2-(n+\frac{1}{2})^2=x-\frac{1}{4} \]

\[(2p-2n-1)(2p+2n+1)=4x-1 \]

然后我们需要把所有 \(4x-1\) 的因数都求出来,求联立方程组

\(a \times b = 4x-1\),其中 \(a,b\) 为整数

\[\begin{cases} 2p-2n-1=a\\ 2p+2n+1=b \end{cases}\]

\[n=\frac{b-a-2}{4} \]

代码
#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^
posted @ 2025-08-25 08:05  huangems  阅读(56)  评论(0)    收藏  举报