AtCoder Beginner Contest 352

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;
}

posted @ 2024-05-05 18:38  view3937  阅读(50)  评论(0编辑  收藏  举报
Title