难忘的瞬间-1见洛谷博客,我是 Timmylyx!
前言:
这一次比赛让我记忆犹新,这是 本蒟蒻 第一次做出 道题呢!
接下来将为大家讲述我的思路,谢谢!
正题:
第 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
这个也不难,我们可以画图分析:
图中的黄色线段、蓝色线段、绿三角(长度为 的线段)分别为黄、蓝、红色
的答案。
具体分类:
- 若
,则结果为
。
- 若
,则结果为
。
- 否则,结果为
。
#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。
我们可以发现,暴力地枚举 不行,时间只允许枚举一个。
剩下的那个怎么不用枚举呢?对了,用一个 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。
可以数出,一共有 个小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
可恶的多测
我们为了解决多测的问题,不能单独记录答案,因为万一 就是当前答案呢?
我们可以考虑,在不爆表的情况下,把能用到的答案记录一下,放进set里。
有一种方法,就是把所有的答案记录一下,但答案就有 种答案,会爆表的,因为有一些状态 没有用。
这里有 种方法,本篇介绍的方法:退而求其次。
当然,如果删除的话,很简单(留给你们啦),主要是添加(一次操作可以看做删除+添加)。
如果 加入序列而且 原序列里没有,那么可以做如下处理:
- 如果
在set里,就把他移除,并且判断
是否存在,若不存在,则将
加入set。
- 将
的次数减少。
至于初始化,就可以看做 次加入了。
还有一个问题,记录次数。如果使用数组,那么这个数组就需要很大,开不下。我们只得使用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
绝杀题目,开始!
乍一看,什么思路都没有 我实在是太菜了 !好在,我看出了破绽……
什么呢?就是二分的条件。可以发现,如果你设置的边长越小,那么就越不容易!
我们可以二分答案,主要如何判断呢?
本蒟蒻看见二维的矩阵,很难受,希望简化维度。
可以发现,行和列可以独立看待(毕竟上下左右互不干扰),我们以行为例:
我们把一个个的点映射到了粗黑线上了,变成了一条直线:
我们每轮把两头的点像中间行进,直到被拦下。第 轮的次数为左右的移动次数的
倍。靠拢的距离是左右移动次数和,直到能被方形的一条边所概括。
#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;
}