AtCoder Beginner Contest 352 - VP记录

A - AtCoder Line

赛时整活想写异或版本的 swap 写错了还 WA 了一发。

不过现在会写了:x^=y^=x^=y

点击查看代码
#include<cstdio>
#include<algorithm>
using namespace std;

int main()
{
	int n,x,y,z;
	scanf("%d%d%d%d",&n,&x,&y,&z);
	if(x>y) swap(x,y);
	printf("%s\n",x<=z&&z<=y?"Yes":"No");
	return 0;
}

B - Typing

一眼 KMP 双指针。

点击查看代码
#include<cstdio>
#include<cstring>
using namespace std;

const int N=2e5+5;
int n,m; char s[N],t[N];

int main()
{
	scanf("%s%s",s+1,t+1);
	n=strlen(s+1),m=strlen(t+1);
	for(int i=1,j=1;j<=m;j++)
		if(s[i]==t[j])
		{
			printf("%d ",j);
			i++;
		}
	return 0;
}

C - Standing On The Shoulders

简单贪心,最后的高度为所有人的肩高加上最上面一个人的头高(身高减肩高),肩高总和无法改变,所以找出最大头高放在最顶上即可。答案为:\(\sum a_i + \max\{b_i-a_i\}\)

点击查看代码
const int N=2e5+5;
int n,a[N],b[N];

int main()
{
	read(n);
	int extra=0;
	long long ans=0;
	for(int i=1;i<=n;i++)
	{
		read(a[i]),read(b[i]);
		ans+=a[i];
		extra=max(extra,b[i]-a[i]);
	}
	ans+=extra;
	write(ans,'\n');
	return 0;
}

D - Permutation Subsequence

先记录每一个数出现的位置 \(pos_{a_i}=i\),然后在数组 \(pos\) 中连续的部分表示的就是 \(a_i\) 值连续的数出现的位置,这其中的最大位置减最小位置就是此时的答案,即答案为:

\[\min_{i=k}^{n}\{ \max_{j=i-k+1}^{i} pos_j - \min_{j=i-k+1}^{i} pos_j \} \]

中间的最大最小值可以用线段树、ST 表等方式求,我这里用的是两个单调队列,一个存最大,一个存最小。

const int N=2e5+5;
int n,k,a[N],pos[N];
int q1[N],head1,tail1;
int q2[N],head2,tail2;

int main()
{
	read(n),read(k);
	for(int i=1;i<=n;i++)
	{
		read(a[i]);
		pos[a[i]]=i;
	}
	int ans=0x3f3f3f3f;
	for(int i=1;i<=n;i++)
	{
		if(head1<=tail1 && i-q1[head1]+1>k) q1[head1++]=0;
		while(head1<=tail1 && pos[i]>pos[q1[tail1]]) q1[tail1--]=0;
		q1[++tail1]=i;
		
		if(head2<=tail2 && i-q2[head2]+1>k) q2[head2++]=0;
		while(head2<=tail2 && pos[i]<pos[q2[tail2]]) q2[tail2--]=0;
		q2[++tail2]=i;
		
		if(i-k+1>=1) ans=min(ans,pos[q1[head1]]-pos[q2[head2]]);
	}
	write(ans,'\n');
	return 0;
}

E - Clique Connect

题目要求求取最小生成树,但是因为每次建立的是完全图,拿什么都存不下,因此肯定不能直接建图跑算法。

回想一下 Kruskal 的思想:每次选择边权最小的边,试图加入树中。而本题中每一个子图完全图中边权是一样的,所以如果要选某个子图完全图中的某一条边,那么其它所有边都需要尝试加入树中。一块完全图处理完毕后,其中所有点都应当是连通的(否则可以在不连通的两块中选一条边加入)。

因此,将所有修改按照边权 \(c\) 从小到大排序,然后从前往后将这块完全图中所有未连通的点连在一起并累加边权即为所求。

维护联通关系可以使用并查集,代码如下:

const int N=2e5+5,M=4e5+5;
int n,m;
pair<int,int> a[M];
vector<int> s[M];

int ufa[N],usz[N];
void uInit()
{
	for(int i=1;i<=n;i++)
		ufa[i]=i,usz[i]=1;
	return;
}
int uquery(int x)
{
	return ufa[x]==x?x:ufa[x]=uquery(ufa[x]);
}
inline void umerge(int x,int y)
{
	x=uquery(x),y=uquery(y);
	if(usz[x]<usz[y]) swap(x,y);
	usz[x]+=usz[y],ufa[y]=x;
	return;
}

int main()
{
	read(n),read(m);
	for(int i=1;i<=m;i++)
	{
		int k; read(k);
		read(a[i].first); a[i].second=i;
		s[i].resize(k);
		for(int j=0;j<k;j++)
			read(s[i][j]);
	}
	sort(a+1,a+m+1);
	long long ans=0;
	uInit();
	for(int i=1;i<=m;i++)
	{
		vector<int> &cur=s[a[i].second];
		for(int j=1;j<(int)cur.size();j++)
			if(uquery(cur[0])!=uquery(cur[j]))
			{
				umerge(cur[0],cur[j]);
				ans+=a[i].first;
			}
	}
	bool is_conn=true;
	int uq1=uquery(1);
	for(int i=1;i<=n;i++)
		if(uquery(i)!=uq1)
		{
			is_conn=false;
			break;
		}
	if(is_conn) write(ans,'\n');
	else puts("-1");
	return 0;
}
posted @ 2024-11-19 17:08  Jerrycyx  阅读(7)  评论(0编辑  收藏  举报