AtCoder Beginner Contest 241 C~F题解

AtCoder Beginner Contest 241

C

题意:给定一个 N ∗ N N*N NN的矩阵,# 表示黑色,”." 表示白色,你最多可以选择两个白色的方块染成黑色,问是否可以最终有6个连续的黑色方块,(水平,垂直,对角线都算)。

因为只需6个连在一起,因此这题可以直接暴力判断,我是用bfs写的,本质上也是暴力,就是代码有点长了。

实在是丑陋!

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second

typedef pair<int,int> pii;
//head

const int N=1e3+10,mod=998244353;

char g[N][N];
bool st[N][N];
int d[4][4]={{0,1,0,-1},{1,0,-1,0},{1,1,-1,-1},{1,-1,-1,1}};
int n;
vector<pii> v;
bool bfs(int sx,int sy,int id)
{
	int cnt=0;
	int sum=0;
	queue<pii> q;
	q.push({sx,sy});
	while(q.size()){
		auto t=q.front();
		q.pop();
		int x=t.x,y=t.y;
		if(st[x][y]) continue;
		sum++;
		if(sum>=6) {
			for(auto [i,j]:v) st[i][j]=0;
			v.clear();
			return 1;
		}
		st[x][y]=1;
		v.push_back({x,y});
		for(int i=0;i<4;i+=2){
			int a=x+d[id][i],b=y+d[id][i+1];
			if(a<0||b<0||a>=n||b>=n||(g[a][b]=='.'&&cnt>=2)) continue;
			if(g[a][b]=='.') cnt++;
			q.push({a,b});
		}
	}
	for(auto [i,j]:v) st[i][j]=0;
	v.clear();
	return 0;
}
void work()
{
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>g[i];
	}
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			if(g[i][j]=='#'){
				for(int k=0;k<4;k++){
					if(bfs(i,j,k)) {
						cout<<"Yes"<<endl;
						return ;
					}
				}
			}
		}
	}
	cout<<"No"<<endl;

}
signed main()
{
	int t;
	//cin>>t;
	t=1;
	while(t--) work();

	return 0;
}

D

题意:给定一个空序列,有三种操作:

  1. 插入 x 插入x 插入x
  2. 询问小于等于 x 的第 k 个数,若不存在输出 − 1 询问小于等于x的第k个数,若不存在输出-1 询问小于等于x的第k个数,若不存在输出1
  3. 询问大于等于 x 的第 k 个数,若不存在输出 − 1 询问大于等于x的第k个数,若不存在输出-1 询问大于等于x的第k个数,若不存在输出1

直接使用 m u l t i s e t multiset multiset即可,可以实现排序,查找,且允许重复元素出现。

注意使用其自带的二分查找。

multiset<int> s;
s.lower_bound(x);//这个是自带的,时间复杂度为O(n*logn)
lower_bound(s.begin(),s.end(),x);// 这个时间复杂度为O(n*logn*logn)

代码即是对操作的模拟。

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second

typedef pair<int,int> pii;
//head
const int N=1e3+10,mod=998244353;
int n;
multiset<int> s;

void work()
{
	int q;
	cin>>q;
	while(q--){
		int op,x,k;
		cin>>op>>x;
		if(op==1){
			s.insert(x);
		}
		else if(op==2){
			cin>>k;
			auto id=s.upper_bound(x);
			while(id!=s.begin()&&k){
				id--; k--;
			}
			if(!k) cout<<*id<<endl;
			else cout<<-1<<endl;
		}
		else {
			cin>>k;
			auto id=s.lower_bound(x);
			
			while(id!=s.end()&&k){
				id++; k--;
			}
			id--;
			if(!k) cout<<*(id)<<endl;
			else cout<<-1<<endl;
		}
	}
}
signed main()
{
	int t;
	//cin>>t;
	t=1;
	while(t--) work();

	return 0;
}

E

题意:给定一个序列A,长度为 N N N, 将做下列操作 K K K次。

X X X为糖果数量(初始为0),每次操作得到 A ( X m o d N ) A_{(XmodN)} AXmodN个糖果。

问操作 K K K次后,得到的糖果总数量为多少。

其中 1 ≤ K ≤ 1 0 12 1\leq K \leq 10^{12} 1K1012.

K很大,暴力做肯定是不行的,因此需要发掘一点性质,由于每次操作都会拿取下标为 X m o d N XmodN XmodN的A,根据抽屉原理,可知最多操作 N + 1 N+1 N+1次,一定会出现重复拿取同一个下标的A。那么这有什么性质呢?假设第二次拿取了 A k A_k Ak,那么接下来拿的顺序我们就可以知道了:即是重复之前的操作,把之前的顺序再重复一遍,也就是会形成一个循环节。

因此只需判断什么时候会形成循环节,然后根据前缀和来计算这一段的总和,再算出循环的次数,就很容易能算出答案

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second

typedef pair<int,int> pii;
//head

const int N=5e5+10,mod=998244353;

int n,k;
int a[N],s[N];
void work()
{
	cin>>n>>k;
	for(int i=0;i<n;i++) {
		cin>>a[i];
	}
	int l,r;
	map<int, int> mp;
	for(int i=1;i<=n+1;i++){
		s[i]=s[i-1]+a[s[i-1]%n];
		if(mp[s[i]%n]){
			l=mp[s[i]%n]+1;
			r=i;
			break;
		}
		mp[s[i]%n]=i;
	}
	int ans=0;
	if(k<=r) {
		ans=s[k];
	}
	else {
		//cout<<l<<endl;
		k-=(l-1);
		ans+=s[l-1];
		int cnt=k/(r-l+1);
		ans+=cnt*(s[r]-s[l-1]);
		int m=k%(r-l+1);
		ans+=s[m+l-1]-s[l-1];
	}
	cout<<ans<<endl;
	
}
signed main()
{
	int t;
	//cin>>t;
	t=1;
	while(t--) work();

	return 0;
}

F

题意:由个 N ∗ M N*M NM的网格,给定起点和终点,同时给出K个障碍物的坐标,问:

是否能从起点到终点,若可以,求出最短的步数。

移动规则为,可以朝上下左右四个方向移动,只有遇到障碍物会停下,否则会滑出网格外,视为无法到达。

只有正好停在终点才算到达终点。

记得之前有场ABC的一题和这一题巨像,那一题是根据指令判断能否到达终点。

注意到要求最短步数,可以想到使用BFS,但是由于N,M比较大,所以不能使用普通的走法,可以用二分来查找障碍物的坐标,然后就是模拟部分了,为了防止判断一些边界情况,插入了两个哨兵,当然不插入也可以。

实际上这题不难,只是我写的代码略微有点丑陋,也有很多其他做法。

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second
typedef pair<int,int> pii;

//head
const int N=1e5+10;

struct node{
	pii p;
	int d;
};
int n,m,q;
int sx,sy,ex,ey;
map<int,set<int>> r,c;
map<pii, int> st;

int ans;
int bfs()
{
	queue<node> q;
	q.push({{sx,sy},0});
	
	while(q.size())
	{
		auto t=q.front().p; 
		int w=q.front().d;
		q.pop();
		if(st[{t.x,t.y}]) continue;
		st[{t.x,t.y}]=1;
		//cout<<"----"<<t.x<<" "<<t.y<<" "<<w<<"---"<<endl;
		if(t.x==ex&&t.y==ey) return w;
		
		int x=t.x,y=t.y;
		int a,b;
	    c[y].insert(-1e18);
        c[y].insert(1e18);
		auto id=c[y].lower_bound(x); id--;
		a=*id+1,b=y;
		if(a!=x&&b==y&&a>0&&a<=n) {
			q.push({{a,b},w+1});//cout<<w+1<<a<<"##"<<b<<endl;
			//if(a==ex&&b==ey) return w+1;
		}
	
	    r[x].insert(-1e18);
        r[x].insert(1e18);
		id=r[x].upper_bound(y); 
		b=*id-1,a=x;
		if(a==x&&b!=y&&b>0&&b<=m) {
			q.push({{a,b},w+1});//cout<<w+1<<a<<"##"<<b<<endl;
			//if(a==ex&&b==ey) return w+1;
		}
	    c[y].insert(-1e18);
        c[y].insert(1e18);
		id=c[y].upper_bound(x); 
		a=*id-1,b=y;
		if(a!=x&&b==y&&a>0&&a<=n) {
			q.push({{a,b},w+1});//cout<<w+1<<a<<"##"<<b<<endl;
			//if(a==ex&&b==ey) return w+1;
		}
	    r[x].insert(-1e18);
        r[x].insert(1e18);
		id=r[x].lower_bound(y);  id--;
		b=*id+1,a=x;
		if(a==x&&b!=y&&b>0&&b<=m) {
			q.push({{a,b},w+1});//cout<<w+1<<a<<"##"<<b<<endl;
			//if(a==ex&&b==ey) return w+1;
		}
		
	}
	return -1;
}
void work()
{
	cin>>n>>m>>q;
	cin>>sx>>sy>>ex>>ey;
	for(int i=1;i<=q;i++){
		int x,y; cin>>x>>y;
		r[x].insert(y);
// 		r[x].insert(-1e18);
// 		r[x].insert(1e18);
		c[y].insert(x);
// 		c[y].insert(-1e18);
// 		c[y].insert(1e18);
	}
	int ans=bfs();
	cout<<ans<<endl;
	
}
signed main()
{
	ios;
	int t;
	t=1;
	while(t--) work();

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值