Codeforces Round 1133(Div.3)题解

A

思路

做法显然,但是需要注意一些小的细节:

①不能输出如"2:2"这样的时间,必须输出"02:02"这样的时间;
②中间的冒号是":",而不是“:”。

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;

int a,b,c,d,n1,n2,n,aa,bb;
char x;

signed main()
{
	cin>>a>>x>>b>>c>>x>>d;
	n1=a*60+b;
	n2=c*60+d;
	n=(n1+n2)/2;
	aa=n/60,bb=n%60;
	
	if (aa<10)  cout<<0<<aa;
	else cout<<aa;
	
	cout<<x;
	
	if (bb<10)  cout<<0<<bb;
	else cout<<bb<<endl; 
	
	return 0;
}

B

思路

用一个桶, b i b_i 记录下除以 k k i i 的数的数量。

显然,若 i + j i+j k k 的倍数,那么可以送给同一个Polycarp女友的盒子对就可以配成 m i n ( b i , b j ) min(b_i,b_j) 组,即可配成 2 m i n ( b i , b j ) 2min(b_i,b_j) 个盒子。

所以,直接枚举 i i ,并求出 j j 使得 i + j i+j k k 的倍数,最后用上面所说的方法求一下就可以啦。注意特判一下 i = j i=j 的情况,可能配成 b i ( b i   m o d   2 ) b_i-(b_i\ mod\ 2) 个盒子。

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;

int n,k,ans;
int a[200005],b[105];

signed main()
{
	cin>>n>>k;
	for (int i=1;i<=n;i++)  cin>>a[i];
	for (int i=1;i<=n;i++)  b[a[i]%k]++;
	
	for (int i=0;i<=k/2;i++)
	{
		int o=(k-i)%k;
		if (i==o)  ans+=(b[i]/2)*2;
		else ans+=min(b[i],b[o])*2; 
	}
	cout<<ans<<endl;
	
	return 0;
}

C

思路

暴力找显然会超时。

那么怎么办呢?首先按升序排序。然后,对于每个 i i 向后找最后一个 a i a_i 之差不大于5的数的位置;记这个位置为 p p ,那么就能形成人数为 p i + 1 p-i+1 的BT(Balanced Team)。

注意"向后找最后一个与 a i a_i 之差不大于5的数的位置"可以用二分来完成。

其时间复杂度为 O ( n l o g 2 n ) O(nlog_2n) ,不会超时。

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;

int n,ans=-1,last;
int a[200005];

int find(int l,int r,int k)
{
	if (l+1==r||l==r)
	{
		if (a[r]-k>5)  return last;
		else return max(r,last);
	}
	int mid=(l+r)>>1;
	if (a[mid]-k<=5)
	{
		last=mid;
		return find(mid,r,k);
	}
	else return find(l,mid,k);
}

signed main()
{
	cin>>n;
	for (int i=1;i<=n;i++)  cin>>a[i];
	
	sort(a+1,a+n+1);
	
	for (int i=1;i<=n;i++)  last=-1,ans=max(ans,find(i,n,a[i])-i+1);
	cout<<ans<<endl;
	
	return 0;
}

D

好惨啊~WA了7次

思路

首先,观察这个式子: d a i + b i = c i da_i+b_i=c_i

c i = 0 c_i=0 带入,得

d a i + b i = 0 da_i+b_i=0
d a i = b i da_i=-b_i
d = b i a i d=\frac {-b_i} {a_i}

所以,对于每个 i i 求出满足要求的 d d ;最后数一数最后有多少个不同的 d d 即可。


这么简单?
怎么可能!

①哇哇!RE!
显然,当 a i = 0 a_i=0 b i 0 b_i≠0 ,是不存在 d d 的,这种情况直接舍去;
另外, a i = b i = 0 a_i=b_i=0 d d 可以随便取值;假设这种情况有 z e r o zero 个,并暂时忽略这种情况,最后将答案加上 z e r o zero 就是最终的答案。

②哇哇!WA!
您的精度出现了问题。
如何控制精度呢?我们可以将分子与分母约分后用map存下。记录下每对约分后分子与分母的相同对数并加上 z e r o zero 就是最终的答案。


代码

#include <bits/stdc++.h>
#define int long long
using namespace std;

int n,ans=-1,last;
int a[200005];

int find(int l,int r,int k)
{
	if (l+1==r||l==r)
	{
		if (a[r]-k>5)  return last;
		else return max(r,last);
	}
	int mid=(l+r)>>1;
	if (a[mid]-k<=5)
	{
		last=mid;
		return find(mid,r,k);
	}
	else return find(l,mid,k);
}

signed main()
{
	cin>>n;
	for (int i=1;i<=n;i++)  cin>>a[i];
	
	sort(a+1,a+n+1);
	
	for (int i=1;i<=n;i++)  last=-1,ans=max(ans,find(i,n,a[i])-i+1);
	cout<<ans<<endl;
	
	return 0;
}

E

思路

一道略简单的 d p dp 题。

状态设计 d p i , j dp_{i,j} 表示,目前看到 i i 这个人,分 j j 个队时最多能进队的人数。

状态转移显然;设 l a s t last i i 之前最前一个 i i 之差不大于 5 5 的数的位置。那么, i i 可以贪心地与 l a s t last 分在一组也可以哪个组都不去。

第一种情况:哪个组都不去。继承上一个状态: d p i 1 , j dp_{{i-1,}j}

第二种情况:与 l a s t last 分在一组。显然,位置在 l a s t + 1 last+1 i i 的位置均要贪心地选。并且,看到 l a s t last 的时候必须贪心地选 j 1 j-1 组,不能选 j j 组,否则 a i a_i 根本进不来,原因是含有 l a s t last 的第 j j 组中一定会有与 a i a_i 之差大于5的数。
所以,此时 d p i , j = d p l a s t , j 1 + ( i l a s t ) dp_{i,j}=dp_{last,{j-1}}+(i-last)

综上所述,状态转移就是:
d p i , j = m a x ( d p i 1 , j , d p l a s t , j 1 + ( i l a s t ) ) dp_{i,j}=max(dp_{i-1,j},dp_{last,{j-1}}+(i-last))

注意,我们不需要用二分找 l a s t last ,朴素找也会超时。总时间复杂度为 O ( n ( n + m ) ) O(n(n+m))

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=505;

int n,m,ans=-1;
int a[maxn],dp[maxn][maxn];

signed main()
{
	cin>>n>>m;
	for (int i=1;i<=n;i++)  cin>>a[i];
	
	sort(a+1,a+n+1);
	dp[1][1]=1;
	
	for (int i=1;i<=n;i++)
	{
		int last=i;
		while (abs(a[last]-a[i])<=5&&last>=1)  last--;
		for (int j=1;j<=m;j++)  dp[i][j]=max(dp[i-1][j],dp[last][j-1]+(i-last));
	}
	for (int i=1;i<=m;i++)  ans=max(ans,dp[n][i]);
	cout<<ans<<endl;
	
	return 0;
}

F1

唉,心态真TM炸了,比赛的时候绞尽脑汁就是想不出来;比赛后仍然想不出来,重新看题才发现本菜鸡把题目看错了

唉,差一点就进前 15 15 了QAQ

题面(罚自己写)

给定一个无向联通图,请找出它的一个生成树使得该树中所有节点的度数的最大值最大,并输出该树的所有边。

思路

显然,我们要贪心地取那个原图中度数最大的节点与其所有边。然后,在保证选出来的是一棵树的情况下,每次任意加边。

注意判断其是否为树(即无环)可以用并查集来完成。

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;

int n,m,u,v,maxv=-1,pos;
int de[200005],father[200005];

struct edge
{
	int u,v;
}e[400005];

int find(int x)
{
	if (x!=father[x])  father[x]=find(father[x]);
	return father[x];
}

signed main()
{
	cin>>n>>m;
	for (int i=1;i<=n;i++)  father[i]=i;
	for (int i=1;i<=m;i++)
	{
		cin>>e[i].u>>e[i].v;
		de[e[i].u]++,de[e[i].v]++;
	}
	for (int i=1;i<=n;i++)
	{
		if (de[i]>maxv)
		{
			maxv=de[i];
			pos=i;
		}
	}
	for (int i=1;i<=m;i++)
	{
		if (e[i].u==pos||e[i].v==pos)
		{
			cout<<e[i].u<<' '<<e[i].v<<endl;
			father[find(e[i].v)]=find(e[i].u);
		}
	}
	for (int i=1;i<=m;i++)
	{
		if (find(e[i].u)!=find(e[i].v)&&e[i].u!=pos&&e[i].v!=pos)
		{
			cout<<e[i].u<<' '<<e[i].v<<endl;
			father[find(e[i].v)]=find(e[i].u);
		}
	}
	return 0;
}

E2

做不出来咕咕咕~

总结

①并查集三个月没做了~这一次又巩固了一遍;
②动规 d p dp 增强了一丢丢;
③信心加了一点点。

太棒了,Div.3我没有爆0!

猜你喜欢

转载自blog.csdn.net/Cherrt/article/details/106882624