ABC330题解(A~F)

[ABC330A] Counting Passes

题意简述

给定 n n n 个数 a 1 , a 2 , ⋯   , a n a_1,a_2,\cdots,a_n a1,a2,,an,问其中有多少个数大于等于 L L L

代码示例

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,L,a[200010],ans=0;
signed main(){
	cin>>n>>L;
	for(int i=1;i<=n;i++) cin>>a[i],ans+=(a[i]>=L);
	cout<<ans<<endl;
	return 0;
}

[ABC330B] Minimize Abs 1

题意简述

给定 n n n 个数 a 1 , a 2 , ⋯   , a n a_1,a_2,\cdots,a_n a1,a2,,an 和两个数 l , r l,r l,r,对于 i = 1 , 2 , ⋯   , n i=1,2,\cdots,n i=1,2,,n,找到满足以下条件的 x i x_i xi

l ≤ x i ≤ r l \leq x_i \leq r lxir 且对于每一个 l ≤ y ≤ r l \leq y \leq r lyr ∣ x i − a i ∣ ≤ ∣ y − a i ∣ \vert x_i -a_i\vert \leq \vert y-a_i \vert xiaiyai

解题思路

发现对于每一个 l ≤ y ≤ r l \leq y \leq r lyr ∣ x i − a i ∣ ≤ ∣ y − a i ∣ \vert x_i -a_i\vert \leq \vert y-a_i \vert xiaiyai 这个条件是很强的。画图找一下就可以发现分三种情况, a i < l a_i<l ai<l l ≤ a i ≤ r l \leq a_i \leq r lair a i > r a_i>r ai>r

代码示例

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,L,R;
int a[200010];
signed main(){
	cin>>n>>L>>R;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++){
		if(a[i]<L) cout<<L<<" ";
		else if(a[i]>R) cout<<R<<" ";
		else cout<<a[i]<<" ";
	}
	return 0;
}

[ABC330C] Minimize Abs 2

题意简述

给定整数 D D D,找出 ∣ x 2 + y 2 − D ∣ \vert x^2 + y^2 -D\vert x2+y2D 的最小值。其中 x , y x,y x,y 是任意非负整数。

做题思路

我们知道一个数的平方一定非负,所以考虑枚举 x x x,再直接用 D − x 2 \sqrt{D- x^2} Dx2 算出 y y y。这个时候 x 2 + y 2 ≤ D x^2+y^2 \leq D x2+y2D,但此时绝对值不一定最小。我们可以将 y + 1 y+1 y+1 然后再算一次,此时 x 2 + y 2 > D x^2+y^2>D x2+y2>D。当 x x x 固定时,最优值一定在这样算出来的两个 y y y 值中取到。

代码示例

#include<bits/stdc++.h>
using namespace std;
#define int long long
int D,ans=0x3f3f3f3f3f3f3f3f;
signed main(){
	cin>>D;
	for(int x=0;x*x<=D;x++){
		int y=sqrt(D-x*x);
		ans=min(ans,abs(D-x*x-y*y));
		y++;
		ans=min(ans,abs(D-x*x-y*y));
	}
	cout<<ans<<endl;
	return 0;
}

[ABC330D] Counting Ls

题意简述

给定一个 n × n n \times n n×n 的矩形,由 ox 组成。找出满足以下条件的三个格子构成的图形(格子不需要连续):

三个格子都由 o 组成,且其中恰好两个在同一行,恰好两个在同一列。

解题思路

其实类似于让我们找 L 形。我们可以枚举 L 形的顶角。如果一个格子 ( i , j ) (i,j) (i,j) 是顶角,那么满足条件的方案数应该为除了 ( i , j ) (i,j) (i,j) 以外,第 i i io 的数量乘上第 j j jo 的数量。所以考虑预处理出每一行和每一列的 o 的数量,枚举顶点直接统计就可以了。

代码示例

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,ans=0;
string s[2010];
int hang[2010],lie[2010];
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>s[i],s[i]=" "+s[i];
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++) hang[i]+=(s[i][j]=='o');
	}
	for(int j=1;j<=n;j++){
		for(int i=1;i<=n;i++) lie[j]+=(s[i][j]=='o');
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(s[i][j]=='o') ans+=(hang[i]-1)*(lie[j]-1);
		}
	}
	cout<<ans<<endl;
	return 0;
}

[ABC330E] Mex and Update

题意简述

给定 n n n 个数 a 1 , a 2 , ⋯   , a n a_1,a_2,\cdots,a_n a1,a2,,an q q q 次询问,每次询问由两个数 i , x i,x i,x 组成。对于每次询问,你需要进行以下操作:

∙ \bullet a i a_i ai 修改为 x x x

∙ \bullet 输出最小的且不被 a a a 包含的非负整数。

解题思路

首先答案的区间应该是 0 0 0 n n n,因为最多只有 n n n 个数,所以超出这个区间的数我们都不用管它。然后用一个 map 维护每个数出现的次数,用一个 set 维护当前不在 a a a 数组中的数有哪些。当 map 的值由 0 0 0 变成 1 1 1 或由 1 1 1 变成 0 0 0,说明我们应该修改 set 中的值了。具体的操作见代码。

代码示例

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,q,a[200010];
map<int,int> vis;
set<int> s;
signed main(){
	cin>>n>>q;
	for(int i=0;i<=n+5;i++) s.insert(i);
	for(int i=1;i<=n;i++){
		cin>>a[i];
		vis[a[i]]++;
		if(a[i]<=n+5&&vis[a[i]]==1) s.erase(s.find(a[i]));
	}
	while(q--){
		int x,y;
		cin>>x>>y;
		vis[a[x]]--;
		if(a[x]<=n+5&&vis[a[x]]==0) s.insert(a[x]);
		a[x]=y;
		vis[a[x]]++;
		if(y<=n+5&&vis[a[x]]==1) s.erase(s.find(a[x]));
		cout<<*s.begin()<<endl;
	}
	return 0;
}

[ABC330F] Minimize Bounding Square

题意简述

给定平面上的 n n n 个点,最多可以进行 k k k 次操作,每次操作可以选定一个点,并将其往上下左右四个方向中的一个移动 1 1 1 单位长度。操作完成后,你需要用一个边长为 l e n len len 的正方形包含所有 n n n 个点。如果一个点在正方形的边或角上,也算做被包含。求出 l e n len len 的最小值。

解题思路

想到了二分答案,但是贪心挂了。

考虑二分正方形的边长 l e n len len,然后计算当正方形的边长为 l e n len len 时需要的最少移动步数是多少,和 k k k 比较。我们发现 x x x 坐标和 y y y 坐标是相互独立的,完全可以分开来算。以下只讨论横坐标的情况。

首先我们在 0 0 0 l e n len len 处放下这个正方形。如果正方形左侧的点比右侧多,就往左侧移,否则往右侧移,直到左右两侧点数量相等。这个贪心策略之所以是正确的,是因为往点多的一侧移动一定会使答案减小。然后我们考虑怎么找到左右两侧点相等的位置。

我们新开一个数组 a a a,对于每个枚举的 l e n len len,往 a a a 中加入 x i x_i xi x i − l e n x_i-len xilen,表示正方形左侧的边位于 x i − l e n x_i-len xilen x i x_i xi 时,这个点可以被包含。然后将 a a a 排序,取最中间的位置就是我们要找的位置了。

代码示例

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,k,x[200010],y[200010],ck[400010],ans=1000000005;
int Getmin(int len,int a[]){
	for(int i=1;i<=n;i++) ck[i]=a[i],ck[i+n]=a[i]-len;
	sort(ck+1,ck+2*n+1);
    //排序
	int l=ck[n],r=ck[n]+len,sum=0;
    //找到正方形的 l,r
	for(int i=1;i<=n;i++) sum+=(l<=a[i]&&a[i]<=r?0:min(abs(a[i]-l),abs(a[i]-r)));
    //统计需要的移动步数
	return sum;
}
signed main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++) cin>>x[i]>>y[i];
	int l=0,r=1000000005;
	while(l<=r){
		int mid=(l+r)>>1;
		if(Getmin(mid,x)+Getmin(mid,y)<=k) r=mid-1,ans=mid;
		else l=mid+1;
        //将 x,y 分开算
	}
	cout<<ans<<endl;
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值