AtCoder Beginner Contest 352
A - AtCoder Line
给 \(N,X,Y,Z\) 判断是否 \(\min(X,Y)\le Z\le \max(X,Y)\)。
模拟。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,x,y,z;
signed main(){
cin>>n>>x>>y>>z; if(x>y) swap(x,y);
if(x<=z&&z<=y) cout<<"Yes";
else cout<<"No";
return 0;
}
B - Typing
给 \(S,T\),而 \(S\) 是 \(T\) 的字典序最小子序列,求 \(S\) 在 \(T\) 中的位置。
模拟。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
char s[200003],t[200003];
signed main(){
cin>>s>>t;
int pos=0;
for(int i=0;t[i];i++){
if(t[i]==s[pos]) cout<<i+1<<' ',pos++;
}
return 0;
}
C - Standing On The Shoulders
有 \(N\) 个巨人,他们的名字分别是 \(1\) 到 \(N\) 。当巨人 \(i\) 站在地上时,他们的肩高是 \(A_i\) ,头高是 \(B_i\) 。
你可以选择 \((1, 2, \ldots, N)\) 的 \((P_1, P_2, \ldots, P_N)\) 排列组合,并根据以下规则堆叠 \(N\) 个巨人:
- 首先,将 \(P_1\) 巨人放在地上。巨人 \(P_1\) 的肩膀距离地面的高度为 \(A_{P_1}\) ,头部距离地面的高度为 \(B_{P_1}\) 。
- 为了 \(i = 1, 2, \ldots, N - 1\) 的顺序,要把巨人 \(P_{i + 1}\) 放在巨人 \(P_i\) 的肩膀上。如果巨人 \(P_i\) 的肩膀距离地面的高度是 \(t\) ,那么巨人 \(P_{i + 1}\) 的肩膀距离地面的高度就是 \(t + A_{P_{i + 1}}\) ,他们的头距离地面的高度就是 \(t + B_{P_{i + 1}}\) 。
求最上面的巨人 \(P_N\) 的头部距离地面的最大可能高度。
贪心,显然肩膀的高度是堆叠的,而头的高度只能算一个,所以把所有肩膀加上以后再找到头到肩膀高度差最大的那个就行。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,a,b,mx,ans;
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a>>b;
ans+=a,mx=max(b-a,mx);
}
cout<<ans+mx;
return 0;
}
D - Permutation Subsequence
给你一个排列 \(P\),求一个长度为 \(k\) 的子序列,使得该子序列的数字是连续的,且子序列头尾位置差最小。
类似滑动窗口,因为要求数字连续,所以我们可以用 set 维护每连续 \(k\) 个数的位置最大最小值,然后向右滑动更新答案即可。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k;
int pos[200003],a[200003];
set<int>s;
signed main(){
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i],pos[a[i]]=i;
}
for(int i=1;i<=k;i++) s.insert(pos[i]);
auto it=s.end(); it--;
int ans=(*it)-(*(s.begin()));
for(int i=k+1;i<=n;i++){
s.erase(pos[i-k]);
s.insert(pos[i]);
it=s.end(); it--;
ans=min(ans,(*it)-(*(s.begin())));
}
cout<<ans;
return 0;
}
E - Clique Connect
给你一个加权无向图 \(G\) ,有 \(N\) 个顶点,编号为 \(1\) 至 \(N\) 。最初, \(G\) 没有边。
您需要执行 \(M\) 次操作来为 \(G\) 添加边。 \((1 \leq i \leq M)\) 的 \(i\) -th 操作如下:
- 给你一个由 \(K_i\) 个顶点组成的顶点子集 \(S_i=\lbrace A_{i,1},A_{i,2},\dots,A_{i,K_i}\rbrace\) 。对于每一对 \(u, v\) ,即 \(u, v \in S_i\) 和 \(u<v\) ,在顶点 \(u\) 和 \(v\) 之间添加一条边,权重为 \(C_i\) 。
完成所有 \(M\) 操作后,确定 \(G\) 是否相连。如果是,求 \(G\) 最小生成树中各条边的总重。
将问题分成两部分,一是判断连通,二是求 MST。
一很好弄,因为所给的每个每个集合之间的块是连通的,直接用并查集处理。
二的话,每个集合里的边是平方级别的,而我们只需要保证连通即可求 MST,所以每个集合里的点只需要保留 \(K_i-1\) 条边即可。最后再对图用 Kruskal 求 MST 即可,时间复杂度 \(O(\sum K\log \sum K)\)。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
int fa[400003],k[400003],c[400003];
vector<int>a[400003];
void init(){for(int i=1;i<=n;i++)fa[i]=i;}
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void merge(int x,int y){fa[find(x)]=find(y);}
bool query(int x,int y){return find(x)==find(y);}
int ffa[400003];
void finit(){for(int i=1;i<=n;i++)ffa[i]=i;}
int ffind(int x){return ffa[x]==x?x:ffa[x]=ffind(ffa[x]);}
void fmerge(int x,int y){ffa[ffind(x)]=ffind(y);}
bool fquery(int x,int y){return ffind(x)==ffind(y);}
struct edge{
int u,v,w,id;
bool operator<(const edge &o)const{return w<o.w;}
}e[800003];int cnt;
signed main(){
cin>>n>>m; init(); finit();
for(int i=1;i<=m;i++){
cin>>k[i]>>c[i];
for(int j=1,A;j<=k[i];j++){
cin>>A;
a[i].push_back(A);
}
for(int j=1;j<k[i];j++){
merge(a[i][j-1],a[i][j]);
e[++cnt]={a[i][j-1],a[i][j],c[i],cnt};
e[++cnt]={a[i][j],a[i][j-1],c[i],cnt};
}
}
for(int i=2;i<=n;i++){
if(!query(i,i-1)){
cout<<"-1\n";
return 0;
}
}
int ans=0;
sort(e+1,e+cnt+1);
for(int i=1;i<=cnt;i++){
if(!fquery(e[i].u,e[i].v)){
fmerge(e[i].u,e[i].v);
ans+=e[i].w;
}
}
cout<<ans;
return 0;
}