[题解]AtCoder Beginner Contest 395(ABC395) A~F
A - Strictly Increasing?
答案为No
\(\iff\)存在\(i\in[1,n)\),使得\(a_i\ge a_{i+1}\)。
点击查看代码
#include<bits/stdc++.h> #define N 110 using namespace std; int n,a[N]; bool check(){ for(int i=1;i<n;i++){ if(a[i]>=a[i+1]) return 0; } return 1; } signed main(){ cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; cout<<(check()?"Yes":"No"); return 0; }
B - Make Target
该题有许多解法。
最好想的:可以直接模拟填色,就不放代码了。
赛时思路(下标从\(1\)开始):我们将每个格子\((i,j)\)都看做坐标\((i,j)\)。那么不难发现一个格子\((x,y)\)的颜色为#
,当且仅当存在\(k\in Z\),使得\(d((x,y),(\frac{n+1}{2},\frac{n+1}{2}))=k+d((1,1),(\frac{n+1}{2},\frac{n+1}{2}))\)。
其中\(d(A,B)=\max(|x_1-x_2|,|y_1-y_2|)\),即\(A,B\)的切比雪夫距离。
这样模拟就可以了,弊端是要开double
,且可读性稍差。
点击查看代码
#include<bits/stdc++.h> using namespace std; int n; signed main(){ cin>>n; double s=(1.0*n+1)/2,o=0; bool p=0; if(n%2==0) o=-0.5; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ bool t=int(max(abs(i-s),abs(j-s))+o)%2; if(i==1&&j==1) p=t; t^=p; cout<<(t?".":"#"); } cout<<"\n"; } return 0; }
题解思路(下标从\(0\)开始):\((i,j)\)为#
,当且仅当\(\min(i,j,n-i,n-j)\)是\(2\)的倍数,因为一个格子是否为#
,只和离它最近的边的距离决定。
点击查看代码
#include<bits/stdc++.h> using namespace std; int n; signed main(){ cin>>n; for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ cout<<((min({i,j,n-i-1,n-j-1})&1)?".":"#"); } cout<<"\n"; } return 0; }
C - Shortest Duplicate Subarray
显然最短的子数组,首位元素必须相同。
所以我们用\(pos_{x}\)表示值\(x\)上一次出现的位置,初始为\(-\infty\)。
从左往右依次遍历每个元素\(a_i\),用\((i-pos_{a_i}+1)\)更新答案,并令\(pos_{a_i}=i\)即可。
时间复杂度\(O(n)\)。
点击查看代码
#include<bits/stdc++.h> #define int long long #define V 1000010 using namespace std; int n,pos[V],ans=LLONG_MAX; signed main(){ memset(pos,-0x3f,sizeof pos); cin>>n; for(int i=1,a;i<=n;i++){ cin>>a; ans=min(ans,i-pos[a]+1); pos[a]=i; } cout<<(ans>n?-1:ans)<<"\n"; return 0; }
D - Pigeon Swap
由于存在交换两个巢的鸽子这一操作,我们不能只记录每只鸽子的位置,而是在这种情况下,直接交换两个巢的位置。
因此,我们定义:
- \(g_i\):第\(i\)个位置是第几个巢。
- \(f_i\):第\(i\)个巢在哪一个位置。
- \(ans_i\):第\(i\)个鸽子在哪个巢。
因此:
- 对于操作\(1\)(将鸽子\(x\)移到巢\(y\)):令\(ans_x=f_y\)。
- 对于操作\(2\)(交换巢\(x,y\)的鸽子):令\(g_{f_y}=x,g_{f_x}=y\),再交换\(f_x,f_y\)。
- 对于操作\(3\)(询问第\(x\)个鸽子在哪个巢):输出\(g_{ans_x}\)。
时间复杂度\(O(n)\)。
点击查看代码
#include<bits/stdc++.h> #define N 1000010 using namespace std; int n,q,ans[N],f[N],g[N]; signed main(){ cin>>n>>q; for(int i=1;i<=n;i++) ans[i]=f[i]=g[i]=i; for(int i=1;i<=q;i++){ int op,x,y; cin>>op; if(op==1) cin>>x>>y,ans[x]=f[y]; else if(op==2) cin>>x>>y,swap(f[x],f[y]),g[f[x]]=x,g[f[y]]=y; else cin>>x,cout<<g[ans[x]]<<"\n"; } return 0; }
E - Flip Edge
建分层图跑最短路即可。
具体来说,建\(2\)层,从下到上节点编号分别是\([1,n],[n+1,2n]\)。
- 下面是原图,上面是反图。
- 另外,对于\(u\in [1,n]\),分别建立\((u,u+n,x),(u+n,u,x)\)两条边。
最终答案即为\(\min(\text{dist}(1,n),\text{dist}(1,2n))\),其中\(\text{dist}(u,v)\)表示\(u\)到\(v\)的最短路。
这样做是正确的,因为跨层走可以理解为花费\(x\)的代价反转了所有边的方向。
代码实现用Dijkstra求最短路,时间复杂度\(O(m\log n)\)。
没太搞懂At上的题解说\(O(n+m)\)的复杂度怎么实现。
注意开long long
,并开\(2\)倍节点。
点击查看代码
#include<bits/stdc++.h> #define int long long #define N 200010 #define PII pair<int,int> using namespace std; struct edge{int to,w;}; vector<edge> G[N<<1]; int n,m,x,d[N<<1]; void add(int u,int v,int w){G[u].emplace_back(edge{v,w});} priority_queue<PII,vector<PII>,greater<PII>> q; void dijkstra(int s){ memset(d,0x3f,sizeof d); d[s]=0,q.push({0,s}); while(!q.empty()){ auto t=q.top(); q.pop(); int u=t.second; if(d[u]<t.first) continue; for(auto i:G[u]){ if(d[u]+i.w<d[i.to]){ d[i.to]=d[u]+i.w,q.push({d[i.to],i.to}); } } } } signed main(){ cin>>n>>m>>x; for(int i=1,u,v;i<=m;i++){ cin>>u>>v; add(u,v,1),add(v+n,u+n,1); } for(int i=1;i<=n;i++) add(i,i+n,x),add(i+n,i,x); dijkstra(1); cout<<min(d[n],d[n<<1]); return 0; }
F - Smooth Occlusion
随着\(h\)增大,答案的合法性是单调变化的,所以考虑二分这个\(h\)。
对于一个\(h\),计算其花费很简单,即\((\sum\limits_{i=1}^n u_i+d_i)-nh\),那么我们要做的只是判断这个\(h\)能否满足“相邻两牙高度差\(\le X\)”这个条件即可。
遍历每一对牙\(i\),这一对牙必须削成\(u_i+d_i=h\),则削完之后,上下牙的夹缝位置(也就是下牙的高度)可能的取值是一个区间:\(B_i=[l=\max(0,h-u_i),r=\min(d_i,h)]\),可以手动模拟理解一下。
初始状态下,这个区间可以认为是\(A=[L=0,R=h]\),每遍历一个\(i\),就令\(A=[L-X,R+X]\ \cap\ B_i\)。
其中\(X\)是输入给的容错率,还是不难理解的。不过注意不是\(A=A\ \cap\ [l-X,r+X]\)。
如果中途\(A=\varnothing\),则此\(h\)不合法。
时间复杂度\(O(n\log V)\)。
点击查看代码
#include<bits/stdc++.h> #define int long long #define N 200010 using namespace std; int n,X,u[N],d[N]; bool check(int h){ int L=0,R=h; for(int i=1;i<=n;i++){ int l=h-u[i],r=d[i]; L=max(L-X,l),R=min(R+X,r); if(L>R) return 0; //必须在循环里判断,因为LR并不是单调变化的 } return 1; } signed main(){ cin>>n>>X; int l=0,r=1e18,sum=0; for(int i=1;i<=n;i++) cin>>u[i]>>d[i], r=min(r,u[i]+d[i]),sum+=u[i]+d[i]; while(l<r){ int mid=(l+r+1)>>1; if(check(mid)) l=mid; else r=mid-1; } cout<<sum-l*n<<"\n"; return 0; }
题解还有\(O(n)\)的做法,这个周四补。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律