Codeforces Round #544 (Div. 3) DEF1F2题解

D. Zero Quantity Maximization

题意:你可以设D为任意实数,使得D*ai+bi为0的数量最多,并求出数量。

思路:很简单,把每组ai bi,都除以他们的gcd然后把ai bi统一换成ai为正数再hash就可以了,不过有一些ai bi为0的细节,也不难去处理,水题。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+10;
map<ll,int>mp,mp2;
int a[maxn],b[maxn],mx,v=1e9+5;
int gcd(int x,int y)
{
	if(!x||!y)return max(x,y);
	return gcd(y,x%y);
}
int main()
{
	int n,res=0,sum=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int j=1;j<=n;j++)scanf("%d",&b[j]);
	for(int i=1;i<=n;i++)
	{
		int x=gcd(abs(a[i]),abs(b[i]));
		if(a[i]==0&&b[i]==0)
		{
			res++;
			continue;
		}
		if(a[i]==0)continue;
		if(b[i]==0)
		{
			sum++;
			mx=max(mx,sum);
			continue;
		}
		a[i]/=x,b[i]/=x;
	
		if(a[i]<0)a[i]=-a[i],b[i]=-b[i];	
		ll cur=1ll*a[i]*v+b[i];
	
		if(1ll*a[i]*b[i]>=0)
		mp[cur]++,mx=max(mx,mp[cur]);
		else 
		mp2[cur]++,mx=max(mx,mp2[cur]);
	}
	cout<<mx+res;
}

E. K Balanced Teams

思路:给你个序列,你可以从中选择一些数组成 k 个集合,每个集合的所有元素之差不能超过5,求 k 个集合最多能有多少元素 。

思路:先排序,然后用单调队列求出从ai开始最多可以走多长的距离并记录为bi,设d[ i ][ j ]为从第 i 个元素到第 n 个元素中组成 j 个集合的最大值,很明显转移方程:d[ i ][ j ]=max(d[ i+1 ][ j ],d[ i+bi ][j-1]+bi)。

#include<bits/stdc++.h>
using namespace std;
const int maxn=5005;
int d[maxn][maxn],a[maxn],b[maxn];
int q[maxn],h,t;
int main()
{
	int n,k,ans=0;
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	sort(a+1,a+1+n);
	for(int i=n;i;i--)
	{
		if(h==t)
		b[i]=1;
		else
		{
			while(a[q[t]]-a[i]>5&&h>t)t++;
			b[i]=h-t+1;
		}
		q[h++]=i;
	}
	for(int i=n;i;i--)
	for(int j=1;j<=k;j++)
	{
		d[i][j]=max(d[i+1][j],d[i][j]);
		d[i][j]=max(d[i+b[i]][j-1]+b[i],d[i][j]);
		ans=max(ans,d[i][j]);
	}
	printf("%d\n",ans);
}

F1. Spanning Tree with Maximum Degree

题意:让你构造一棵树,使得该树度数最大的节点度数最大。

思路:先找到度数最大的点rt,先把连接rt的边全部连接,然后再用并查集判环去连接剩下的边即可,水题。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+10;
vector<int>G[maxn];
int a[maxn],b[maxn],p[maxn],d[maxn];
int find(int x)
{
	if(x!=p[x])
	p[x]=find(p[x]);
	return p[x];
}
int Union(int u,int v)
{
	int fu=find(u);
	int fv=find(v);
	if(fu!=fv)
	{
		p[fu]=fv;
		return 1;
	}
	return 0;
}
int main()
{
	int n,m,rt,mx=0;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)p[i]=i;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&a[i],&b[i]);
		d[a[i]]++,d[b[i]]++;
		if(d[a[i]]>mx)
		mx=d[a[i]],rt=a[i];
		if(d[b[i]]>mx)
		mx=d[b[i]],rt=b[i];
		G[a[i]].push_back(b[i]);
		G[b[i]].push_back(a[i]); 
	}
	for(int i=0;i<G[rt].size();i++)
	{
		printf("%d %d\n",rt,G[rt][i]);
		Union(G[rt][i],rt);
	}
	n-=G[rt].size();
	if(n>1)
	{
		for(int i=1;i<=m;i++)
		{
			if(Union(a[i],b[i]))
			{
				printf("%d %d\n",a[i],b[i]);
				n--;
				if(n==1)return 0;
			}
		}
	}
}

F2. Spanning Tree with One Fixed Degree

题意:要求根节点即1号点度数为k,让你构造一棵树。

思路:先把1号点相连的点全部标记,然后进行第一次连边:枚举所有边u v,如果u v没有同时被标记且均不为根就连接,第二次连边:枚举所有边 u v,如果都被标记了且还需要连接的边>k,就连接,第三次连边:连接1和被标记的节点即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10,inf=1e9;
vector<int>G[maxn];
int a[maxn],b[maxn],p[maxn],vis[maxn];
int t1[maxn],t2[maxn],cnt;
int find(int x)
{
	if(x!=p[x])
	p[x]=find(p[x]);
	return p[x];
}
int Union(int u,int v)
{
	int fu=find(u),fv=find(v);
	if(fu!=fv)
	{
		if(vis[fu])p[fv]=fu;
		else p[fu]=fv;
		return 1;
	}
	return 0;
}
int main()
{
	int n,m,k;
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=n;i++)p[i]=i;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&a[i],&b[i]);
		G[a[i]].push_back(b[i]);
		G[b[i]].push_back(a[i]); 
	}
	for(int i=0;i<G[1].size();i++)
	vis[G[1][i]]=1;
	if(n-1>k)
	for(int i=1;i<=m;i++)
	{
		int fa=find(a[i]),fb=find(b[i]);
		if(vis[fa]&&vis[fb])continue;
		if(a[i]!=1&&b[i]!=1&&Union(a[i],b[i]))
		{
			t1[++cnt]=a[i],t2[cnt]=b[i];
			n--;
			if(n-1==k)
			break;
		}
	}
	if(n-1>k)
	for(int i=1;i<=m;i++)
	{
		int fa=find(a[i]),fb=find(b[i]);
		if(vis[fa]&&vis[fb])			
		{
			if(a[i]!=1&&b[i]!=1&&Union(a[i],b[i]))
			{
				t1[++cnt]=a[i],t2[cnt]=b[i];
				n--;
				if(n-1==k)
				break;
			}
		}
	}
	for(int i=0;i<G[1].size();i++)
	if(Union(1,G[1][i]))
	{
		t1[++cnt]=1,t2[cnt]=G[1][i];
		n--,k--;
		if(n==1||!k)break;
	}
	if(n>1)puts("NO"),exit(0);
	else
	{
		puts("YES");
		for(int i=1;i<=cnt;i++)
		printf("%d %d\n",t1[i],t2[i]);
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙橘子猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值