[题解]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)\)的做法,这个周四补。

posted @   Sinktank  阅读(454)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
2025-3-10 16:37:46 TOP-BOTTOM-THEME
Enable/Disable Transition
Copyright © 2023 ~ 2024 Sinktank - 1328312655@qq.com
Illustration from 稲葉曇『リレイアウター/Relayouter/中继输出者』,by ぬくぬくにぎりめし.
点击右上角即可分享
微信分享提示