0 简介
0-1 关于文章
这是 AtCoder Beginner Contest 272 的解释。
该实现是用 Python 和 C++ 编写的。
请注意,可能与官方解释存在差异。
如果您发现任何错误,请在评论部分告诉我。
1 ABC272 评论
1-1 个人印象
这是一个可悲的时代。我在 19 分钟内到达 D,但我就是无法超过 E。
Diff就像A是灰色的前半部分,B和C是灰色的后半部分,D是绿色的前半部分,E是水的后半部分,F是黄色。
无论如何,我为E感到难过。我几乎能够实现它,但是......
1-2 问题整数和
问题
给定一个包含 $N$ 项的序列 $A$,求 $A$ 的总和。
约束
・$1 leq N leq 100$
・$1 列克 A_i 列克 1000 美元
评论
您只需要输入并添加它。
Python中的实现示例
N=int(input())
print(sum(list(map(int,input().split()))))
不要让) 的号码困扰您。
C++中的实现示例
#include <bits/stdc++.h>
using namespace std;
#define rep(i,N,M) for(int i=N; i<M; i++)
int main(){
int N; cin>>N;
int ans=0;
rep(i,0,N){
int a; cin>>a;
ans+=a;
}
cout<<ans<<endl;
}
即使在 C++ 中实现,int 类型也很好,因为 $A_i$ 的大小不是什么大问题。
1-3 问题 B 每个人都是朋友
问题
有$N$人。
举行了 $M$ 个舞会,$k_i$ 人参加了第 $i$ 个舞会,$x_{i,j}$ 人参加了。
确定是否有任何一对 2 美元的人参加过同一个球至少 1 美元次。
约束
・$2 leq N leq 100$
・$1 leq M leq 100$
・$2 leq k_i leq N$
・$1 leq x_1 leq x_2 leq ... leq x_{k_i} leq N$
评论
遍历所有可能的 $2$ 人对,并确定这对人是否参加了同一个舞会。其中,如果有一组从未去过同一个球,那就是No,否则就是Yes。
使用处理集合的类型(set 用于 Python,unordered_set 用于 C++)进行判断。
复杂度为 $O(N^2 M)$。
Python中的实现示例
N,M=map(int,input().split())
X=[]
for i in range(M):
k,*x=list(map(int,input().split()))
X.append(set(x))
for i in range(N):
for j in range(N):
fl=0
for k in range(M):
if i+1 in X[k] and j+1 in X[k]:
fl=1
if not fl:
print("No")
exit()
print("Yes")
* 很有用。
C++中的实现示例
#include <bits/stdc++.h>
using namespace std;
#define rep(i,N,M) for(int i=N; i<M; i++)
int main(){
int N,M; cin>>N>>M;
vector<set<int>> X(M);
rep(i,0,M){
int k; cin>>k;
rep(j,0,k){
int x; cin>>x;
X[i].insert(x);
}
}
rep(i,0,N){
rep(j,0,N){
bool fl=false;
rep(k,0,M){
if(X[k].count(i+1)>0 and X[k].count(j+1)>0){
fl=true;
}
}
if(!fl){
cout<<"No"<<endl;
return 0;
}
}
}
cout<<"Yes"<<endl;
}
1-4 问题 C 最大偶数
问题
给定一个长度为 $N$ 的非负整数序列 $A$。
确定 $A$ 的不同 $2$ 元素的可能总和是否为偶数,如果是,则找出其中最大的。
约束
・$2 leq N leq 2×10^5$
・$0 列克 A_i 列克 10^9$
・$A$的元素不同
评论
如果您天真地搜索所有 $2$ 数字对,它将是 $O(N^2)$,您将无法及时找到它。
让我们设计它。
首先,如果有 $2$ 个整数 $N,M$ 并且 $N+M$ 是偶数,则 $N,M$ 是偶数和奇数。
因此,如果 $A$ 的不同 $2$ 元素的可能总和中存在偶数,那么当我们将 $A$ 拆分为偶数列 $B$ 和奇数列 $C$、$B$ 或 $C $either长度必须大于或等于$2$。
此外,当存在时,这些数字的最大值分别是 $B 和 $C 的两个总和中的较大者。
你应该实现这个。
将$A$平均分成$B$和$C$是$O(N)$,找到可能的最大值需要排序,所以$O(N log N)$。
总计算复杂度为$O(N log N)$。
与其分成 $B$ 和 $C$,不如将其实现为一个二维数组似乎更容易。
Python中的实现示例
N=int(input())
A=list(map(int,input().split()))
L=[[] for _ in range(2)]
for n in A:
L[n%2].append(n)
ans=-1
for i in range(2):
if len(L[i])>=2:
L[i].sort()
ans=max(L[i][-1]+L[i][-2],ans)
print(ans)
C++中的实现示例
#include <bits/stdc++.h>
using namespace std;
#define rep(i,N,M) for(int i=N; i<M; i++)
template <typename T>
T chmax(T &x, const T& y){
if(x<y){x=y; return true;}
return false;
}
int main(){
int N; cin>>N;
vector<vector<int>> L(2);
rep(i,0,N){
int a; cin>>a;
L[a%2].push_back(a);
}
int ans=-1;
rep(i,0,2){
if(L[i].size()>=2){
sort(L[i].rbegin(),L[i].rend());
chmax(ans,L[i][0]+L[i][1]);
}
}
cout<<ans<<endl;
}
1-5 D 问题 Root M Leaper
问题
坐标上有$N×N$个正方形。
首先,一块放在正方形 $(1,1)$ 上。您可以执行以下任何操作:
・将棋子移动到距离棋子放置位置正好 $sqrt{M}$ 的网格点。
现在假设$(i,j),(k,l)$之间的距离是$sqrt{(i-k)^2+(j-l)^2}$。
对所有正方形 $(i,j)$ 求解以下问题。・求将棋子从 $(1,1)$ 移动到 $(i,j)$ 所需的操作数。
如果仍然无法访问,请报告。约束
・$1 leq N leq 400$
・$1 leq M leq 10^6$评论
“问题”可以通过 BFS 解决,但至少需要 $O(N^2)$ 才能解决一次,所以 $N^2$
如果您解决每个问题,它将是 $O(N^4)$ 并且您似乎无法及时解决。现在,让我们考虑在一次操作中计算可以从正方形 $(a,b)$ 移动的正方形。
从问题的规范来看,如果$sqrt{i^2+j^2}=sqrt{M}$,即${i^2+j^2=M}$,$(a,b)$为$ (a+i,b+j)$。
由于每个 $i,j$ 都是一个整数,因此将 for 语句传递给 $i$ 并确定是否存在诸如 ${i^2+j^2=M}$ 的整数 $j$ 就足够了。这可以通过 $O(1)$ 来完成。
由于$M$ 的限制,这个计算可以在$O(M)$ 中完成。现在我们知道一步可以移动到哪里,我们使用该移动来找到到每个方格的最短距离。
最好使用您之前找到的以 $(1,1)$ 为起点进行 BFS。找到到所有 $N^2$ 个正方形的最短距离。这次的方格数是$N^2$,目的地并不多。从 ${i^2+j^2=M}$,它将是与 $O(sqrt{M})$ 成比例的数量。
所以这个问题的复杂度是$O(N^2 sqrt{M})$。找到目的地时,限制$i,j > 0$,然后找到$(i,j),(i,-j),(-i,j),(-i,- 很容易注册j)$ .
Python中的实现示例
from collections import deque N,M=map(int,input().split()) able=[] for i in range(M+1): n=M-i**2 if n<0: break n=n**0.5 if int(n)==n: n=int(n) able.append([i,n]) able.append([i,-n]) able.append([-i,n]) able.append([-i,-n]) D=deque(); D.append([0,0]) dist=[[-1]*N for _ in range(N)]; dist[0][0]=0 ok=lambda x,y:0<=x<N and 0<=y<N and dist[x][y]==-1 while D: x,y=D.popleft() for a,b in able: nx,ny=x+a,y+b if not ok(nx,ny): continue dist[nx][ny]=dist[x][y]+1 D.append([nx,ny]) for n in dist: print(*n,sep=" ")这一次,一次可以移动的方格数和每次移动的方向是相同的,所以我们可以通过先计算来减少计算量。C++中的实现示例
#include <bits/stdc++.h> using namespace std; #define rep(i,N,M) for(int i=N; i<M; i++) int main(){ int N,M; cin>>N>>M; vector<pair<int,int>> able; vector<int> pm={1,-1}; rep(i,0,M+1){ int n=M-i*i; if(n<0){ break; } int m=sqrt(n); if(m*m==n){ rep(a,0,2) rep(b,0,2){ able.push_back(make_pair(i*pm[a],m*pm[b])); } } } deque<pair<int,int>> D; D.push_back(make_pair(0,0)); vector<vector<int>> dist(N,vector<int>(N,-1)); dist[0][0]=0; while(!D.empty()){ int x,y; tie(x,y)=D.front(); D.pop_front(); for(auto [a,b]:able){ int nx=x+a,ny=y+b; if(!(0<=nx and nx<N and 0<=ny and ny<N and dist[nx][ny]==-1)){ continue; } dist[nx][ny]=dist[x][y]+1; D.push_back(make_pair(nx,ny)); } } rep(i,0,N){ rep(j,0,N){ cout<<dist[i][j]; if(j==N-1){ cout<<endl; }else{ cout<<" "; } } } }1-6 E 题 Add and Mex
问题
给定一个长度为 $N$ 的整数序列 $A$。
执行以下 $M$ 次。・对于每个 $i$,将 $i$ 添加到 $A_i$。找到 $A$ 的 mex。
请注意,序列的 mex 是不包含在序列中的最小非负整数。
约束
・$1 leq N,M leq 2×10^5$
・$1 列克 A_i 列克 10^9$评论
墨西哥
让我们先谈谈墨西哥。
正如问题陈述所说,mex 是给定整数序列中不包含的最小非负正数。
例如,$[0,1,2,4]$ 的 mex 是 $3$,$[1,2,4]$ 的 mex 是 $0$。
如果你想一想,你会发现给定整数序列的长度是 $n$,并且该整数序列的 mex 大于或等于 $0$ 且小于或等于 $n$。
因此,通过将 for 语句从 $0$ 传递到 $n$ 并检查是否存在,可以在 $O(n)$ 中找到 mex 的值。
下面是一个查找 mex 的程序。它可能很少使用,所以把它放在图书馆里可能会很好。在python中找到mex
def mex(L): N=len(L) s=set(L) for i in range(N+1): if i not in s: return i在 C++ 中查找 mex
#include <bits/stdc++.h> using namespace std; #define rep(i,N,M) for(int i=N; i<M; i++) int mex(vector<int> L){ int N=L.size(); unordered_set<int> S; for(auto i:L) S.insert(i); rep(i,0,N+1){ if(!S.count(i)){ return i; } } }主要科目
回到主题。
如果只是简单的执行指定的操作$M$次,每次操作都会花费$O(N)$,所以总的计算量会是$O(NM)$,这是不够的。
我现在该怎么办。让我们考虑一个具体的例子。这里我们使用输入示例 2。
$A_i$ 和操作数的表。
正如我在开头提到的,mex 的值只能在$0$ 和$n$ 之间,其中$n$ 是序列的长度。结果保持不变。即使在这种情况下,序列的长度也是 $N$,所以为了找到 mex,我们只需要查看 $0$ 和 $N$ 之间的元素。
事实上,让我们只给 $0$ 和 $N$ 之间的东西上色。
从左到右,可看的更少。
$i$ 列中要查看的数字最多为 $lfloor { frac{N}{i}} floor $ 件(准确地说,$lfloor { frac{N+1}{i}} 地板$)。
如果你考虑一下,$lfloor { frac{N}{i}} 那是地板 $ 件,不是吗?所以要查看的总数最多为 $N log_2 N$。
为什么?
$N log_2 N$ 可以通过将公式转换如下得出。
$ frac{1}{1} + frac{1}{2} + frac{1}{3} + frac{1}{4} + frac{1}{5} + frac{1}{6} + frac {1}{7}+frac{1}{8}+...$
$ leq frac{1}{1} + frac{1}{2} + frac{1}{2} + frac{1}{4} + frac{1}{4} + frac{1}{4}+压裂{1}{4}+压裂{1}{8}+...$
$ = 1 + 1 + 1 + 。 . . $稍微想一想,可以看到最后一个表达式中$1$的个数是$ lfloor {log_2 N} 地板$。
所以第一个表达式 $frac{1}{1}+frac{1}{2}+frac{1}{3}+frac{1}{4}+frac{1}{5}+frac{1} { 6}+frac{1}{7}+frac{1}{8}+...+frac{1}{N}$ 小于 $log_2 N$。因此,我们现在应该寻找 $lfloor { frac{N}{1}} 地板 +lfloor {frac{N}{2}} 地板 +lfloor {frac{N}{3}} 地板 +lfloor {frac{N}{4}} 地板 ...lf 地板 {frac{N}{N}} floor $ 显然小于 $N log_2 N$。
查看图表,您可以看到情况确实如此。 (蓝色的是$N log_2 N$)由于要查看的数字总数为$N log_2 N$,因此可以只获取要查看的数字,然后在操作次数为$j$时获取每个mex。
由于mex可以通过$O(n)$得到,其中$n$是序列的大小,所以mex部分的计算复杂度为$O(N log N)$。要查看要查看的数字,我们只需为每个 $A_i$ 计算以下内容。
・$A_i$大于或等于$0$(大于$0$且小于$M$)的最小操作数
・$A_i$为$N$以下的最大操作数($0$以上且$M$以下)这是通过除法计算的。
如果您要求它,请使用 for 语句将其转过来。
如上所述,要查看的数字总共小于$N log_2 N$,因此这部分的复杂度为$O(N log N)$。因此,总的计算复杂度为$O(N log N)$,足够了。
你应该实现这个。
方便地创建一个数组来存储 $0$ 和 $N$ 之间的数字,当操作数为 $j$ 时可以创建该数组。 (实现示例中的 $L$)Python中的实现示例
def mex(L): N=len(L) s=set(L) for i in range(N+2): if i not in s: return i def divceil(x,y): return (x+y-1)//y N,M=map(int,input().split()) A=list(map(int,input().split())) L=[[] for _ in range(M+1)] for i in range(N): l,r=0,0 if A[i]<0: l=divceil(-A[i],i+1) else: l=1 r=min(M+1,divceil(N-A[i],i+1)) for j in range(l,r): num=j*(i+1)+A[i] L[j].append(num) for i in range(1,M+1): print(mex(L[i]))分割部分的实现相对困难。 请记住,$1$ 到 $N$ 的倒数之和(称为调和级数)是 $O(log N)$,这种情况很少见。C++中的实现示例
#include <bits/stdc++.h> using namespace std; #define rep(i,N,M) for(int i=N; i<M; i++) int mex(vector<int> L){ int N=L.size(); unordered_set<int> S; for(auto i:L) S.insert(i); rep(i,0,N+1){ if(!S.count(i)){ return i; } } } int divceil(int x,int y){ return (x+y-1)/y; } int main(){ int N,M; cin>>N>>M; vector<int> A(N); rep(i,0,N) cin>>A[i]; vector<vector<int>> L(M+1); rep(i,0,N){ int l,r; if(A[i]<0){ l=divceil(-A[i],i+1); }else{ l=1; } r=min(M+1,divceil(N-A[i],i+1)); rep(j,l,r){ int num=j*(i+1)+A[i]; L[j].push_back(num); } } rep(i,1,M+1){ cout<<mex(L[i])<<endl; } }
unordered_set的计算量似乎不稳定。
由于某种原因,执行速度比 PyPy 慢。
请告诉我更多关于它的信息..谢谢我意识到一共有$O(N log N)$,但最后我没有意识到mex的计算复杂度也是$O(N log N)$,我一直陷入困境,无法没解决。
我想解决它,因为它是一个水差异,但是......2 最后
感谢您阅读到最后。
A、B 和 C 是灰色 diff,D 也是绿色 diff,所以有些部分很快就解决了,但我认为这是一个有趣的时间。
特别要记住 D,“先计算目的地”,因为它是一项重要的技术。
换个话题,我不是很了解unordered_set的计算量,所以如果你知道更多,请在评论中告诉我。
就这样。
有一个良好的职业竞争生活!请喜欢...(绝望)
原创声明:本文系作者授权爱码网发表,未经许可,不得转载;
原文地址:https://www.likecs.com/show-308628792.html