(AtCoder Beginner Contest 330) 题解

 难忘的瞬间-1见洛谷博客,我是 Timmylyx!

前言:

这一次比赛让我记忆犹新,这是 本蒟蒻 第一次做出 6 道题呢!

接下来将为大家讲述我的思路,谢谢!

正题:

第 1 题,Counting Passes

这真是一道水题呀!

按照题意模拟即可!

#include <bits/stdc++.h>
using namespace std;
int n,m,x,cnt;
signed main(){
	cin>>n>>m;
	while (n--){
		cin>>x; if (x>=m) cnt++;
	}cout<<cnt; 
	return 0;
}

(喜爱压行,不喜勿喷)

第 2 题,Minimize Abs 1

这个也不难,我们可以画图分析:

图中的黄色线段、蓝色线段、绿三角(长度为 0 的线段)分别为黄、蓝、红色 a_{i} 的答案。

具体分类:

  • 若 a_{i}<l ,则结果为 l
  • 若 l \le a_{i} \le r,则结果为 a_{i}
  •  否则,结果为 r

#include <bits/stdc++.h>
using namespace std;
#define int long long
int n,m,l,r,x;
set <int> st;
signed main(){
	cin>>n;
	for (int i=0; (i-1)*(i-1)<=n; i++){
		st.insert(i*i);
	}int ans=n;
	for (auto x:st){
		m=n-x; auto it=st.lower_bound(m);
		auto it2=it; it2--;
		ans=min(ans,min(abs(m-*it),abs(m-*(it2))));
		//cout<<m<<" "<<abs(m-*it)<<" "<<abs(m-*(it2))<<endl;
	}cout<<ans;
	return 0;
}

第 3 题,Minimize Abs 2

考察数据结构 set。

我们可以发现,暴力地枚举 x^2,y^2 不行,时间只允许枚举一个。

剩下的那个怎么不用枚举呢?对了,用一个 set 存起来,然后二分即可!

#include <bits/stdc++.h>
using namespace std;
#define int long long
int n,m,l,r,x;
set <int> st;
signed main(){
	cin>>n;
	for (int i=0; (i-1)*(i-1)<=n; i++){
		st.insert(i*i);
	}int ans=n;
	for (auto x:st){
		m=n-x; auto it=st.lower_bound(m);
		auto it2=it; it2--;
		ans=min(ans,min(abs(m-*it),abs(m-*(it2))));
		//cout<<m<<" "<<abs(m-*it)<<" "<<abs(m-*(it2))<<endl;
	}cout<<ans;
	return 0;
}

注意初始化要留出左右一个的数字,以防发生错误。

第 4 题,Counting Ls

先说一下,题目里的要求你找出三个点都是 o 的小L字形。 

我们可以绘制如下矩阵: 

红星是拐角处,上下左右各有一个 o。

可以数出,一共有 4 个小L。

稍加思考,可以发现,我们从红色行中可红色列中找到两个非红星的 o 时,他就是一组解。

依据乘法原理,个数等于行个数乘以列个数。

但是暴力地枚举行、列会超时,我们可以进行预处理,处理出每行每列有几个 o!

#include <bits/stdc++.h>
using namespace std;
#define int long long
int n,cnth[2020],cntl[2020]; 
char c[2020][2020];
signed main(){
	cin>>n;
	for (int i=1; i<=n; i++){
		for (int j=1; j<=n; j++){
			cin>>c[i][j];
			if (c[i][j]=='o'){
				cnth[i]++; cntl[j]++;
			}
		}
	}int ans=0;
	for (int i=1; i<=n; i++){
		for (int j=1; j<=n; j++){
			if (c[i][j]=='o'){
				ans+=(cnth[i]-1)*(cntl[j]-1);
			}
		}
	}cout<<ans;
	return 0;
}

第 5 题,Mex and Update

可恶的多测

我们为了解决多测的问题,不能单独记录答案,因为万一 x_{i} 就是当前答案呢?

我们可以考虑,在不爆表的情况下,把能用到的答案记录一下,放进set里。

有一种方法,就是把所有的答案记录一下,但答案就有 10^9-n 种答案,会爆表的,因为有一些状态 没有用

这里有 2 种方法,本篇介绍的方法:退而求其次

当然,如果删除的话,很简单(留给你们啦),主要是添加(一次操作可以看做删除+添加)。

如果 x 加入序列而且 原序列里没有,那么可以做如下处理:

  1. 如果 x 在set里,就把他移除,并且判断 x+1 是否存在,若不存在,则将 x+1 加入set。
  2. 将 x 的次数减少。

 至于初始化,就可以看做 n 次加入了。

还有一个问题,记录次数。如果使用数组,那么这个数组就需要很大,开不下。我们只得使用map(丢失时间找回空间)。

#include <bits/stdc++.h>
using namespace std;
#define int long long
int a[200010],ans,id,x,y,n,q;
map<int,int> cnt;
signed main(){
	cin>>n>>q;set <int> s;
	for (int i=1; i<=n; i++){
		cin>>a[i]; cnt[a[i]]++;
	}a[0]=-1;
	for (int i=0; i<=n; i++){
		if (!cnt[a[i]+1]) s.insert(a[i]+1);
	}
	for (int i=1; i<=q; i++){
		cin>>id>>y; x=a[id]; a[id]=y;
		cnt[x]--; if(!cnt[x]) s.insert(x); 
		cnt[y]++; if(s.count(y)) {
			s.erase(y); if(!cnt[y+1]) s.insert(y+1);
		}
		cout<<*(s.begin())<<"\n";
	}
	return 0;
}

第 6 题,Minimize Bounding Square

绝杀题目,开始!

乍一看,什么思路都没有 我实在是太菜了 !好在,我看出了破绽……

什么呢?就是二分的条件。可以发现,如果你设置的边长越小,那么就越不容易!

我们可以二分答案,主要如何判断呢?

本蒟蒻看见二维的矩阵,很难受,希望简化维度。

可以发现,行和列可以独立看待(毕竟上下左右互不干扰),我们以行为例:

我们把一个个的点映射到了粗黑线上了,变成了一条直线:

我们每轮把两头的点像中间行进,直到被拦下。第 i 轮的次数为左右的移动次数的 i 倍。靠拢的距离是左右移动次数和,直到能被方形的一条边所概括。

#include <bits/stdc++.h>
using namespace std;
#define int long long
int x[200010],y[200010],a,b,n,m;
bool check(int mid){
	int ans=0,tmp=x[n]-x[1]-mid;
	for (int i=1; i<=n; i++){
		int cs=x[i+1]-x[i]+x[n-i+1]-x[n-i];
		if (tmp<=0) break;
		else if(tmp<=cs){
			ans+=tmp*i; tmp=0;
		}else{
			ans+=cs*i; tmp-=cs;
		}
	}
	tmp=y[n]-y[1]-mid;
	for (int i=1; i<=n; i++){
		int cs=y[i+1]-y[i]+y[n-i+1]-y[n-i];
		if (tmp<=0) break;
		else if(tmp<=cs){
			ans+=tmp*i; tmp=0;
		}else{
			ans+=cs*i; tmp-=cs;
		}
	}return ans<=m;
}
signed main(){
	cin>>n>>m;
	for (int i=1; i<=n; i++){
		cin>>a>>b; x[i]=a; y[i]=b; 
	}
	sort(x+1,x+n+1); sort(y+1,y+n+1);
	int l=0,r=1000000000;
	while (l<r-1){
		int mid=(l+r)/2;
		if (check(mid)) r=mid;
		else l=mid;
	}
	if (check(l)) cout<<l;
	else cout<<r;
	return 0;
}

  • 48
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值