[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 l≤xi≤r 且对于每一个 l ≤ y ≤ r l \leq y \leq r l≤y≤r, ∣ x i − a i ∣ ≤ ∣ y − a i ∣ \vert x_i -a_i\vert \leq \vert y-a_i \vert ∣xi−ai∣≤∣y−ai∣。
解题思路
发现对于每一个 l ≤ y ≤ r l \leq y \leq r l≤y≤r, ∣ x i − a i ∣ ≤ ∣ y − a i ∣ \vert x_i -a_i\vert \leq \vert y-a_i \vert ∣xi−ai∣≤∣y−ai∣ 这个条件是很强的。画图找一下就可以发现分三种情况, a i < l a_i<l ai<l, l ≤ a i ≤ r l \leq a_i \leq r l≤ai≤r, 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+y2−D∣ 的最小值。其中 x , y x,y x,y 是任意非负整数。
做题思路
我们知道一个数的平方一定非负,所以考虑枚举 x x x,再直接用 D − x 2 \sqrt{D- x^2} D−x2 算出 y y y。这个时候 x 2 + y 2 ≤ D x^2+y^2 \leq D x2+y2≤D,但此时绝对值不一定最小。我们可以将 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 的矩形,由 o
和 x
组成。找出满足以下条件的三个格子构成的图形(格子不需要连续):
三个格子都由 o
组成,且其中恰好两个在同一行,恰好两个在同一列。
解题思路
其实类似于让我们找 L
形。我们可以枚举 L
形的顶角。如果一个格子
(
i
,
j
)
(i,j)
(i,j) 是顶角,那么满足条件的方案数应该为除了
(
i
,
j
)
(i,j)
(i,j) 以外,第
i
i
i 行 o
的数量乘上第
j
j
j 列 o
的数量。所以考虑预处理出每一行和每一列的 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 xi−len,表示正方形左侧的边位于 x i − l e n x_i-len xi−len 到 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;
}