Codeforces Round #513 by Barcelona Bootcamp (rated, Div. 1 + Div. 2)部分题解

Codeforces Round #513 by Barcelona Bootcamp (rated, Div. 1 + Div. 2)ABCD题题解

前言

话说CF的比赛在下午真的是千年难遇,所以我和几个同学组团,去打了CF,还创了一个流毒的群:
在这里插入图片描述
考试的样例解释彻底把我带偏了。。。导致我半个多小时耗在B题。。。。

做C题时,某位大佬乱说了一句滑窗,导致我卡了一个多小时QAQ。。。。。

但是我虽然回了一点rating,我还是绿的QAQ。。。

A. Phone Numbers

◇题目传送门◆

题目大意

给定 N N N个一位整数,要求用这些数组成形如8xxxxxxxxx的数,其中x代表任意一位整数。求最多能组成的数的组数。

思路

仔细分析题目不难发现,组数只和数字8的个数与 ⌊ N 11 ⌋ \lfloor\frac{N}{11}\rfloor 11N有关,所以不难得出正解。

正解代码

#include
#include
using namespace std;

const int Maxn=100;

int N;
char s[Maxn+5];

int main() {
	#ifdef LOACL
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
	#endif
	scanf("%d\n",&N);
	scanf("%s",s+1);
	int sum8,sum;
	sum8=sum=0;
	for(int i=1;i<=N;i++)
		if(s[i]=='8')sum8++;
	printf("%d\n",min(sum8,N/11));
	return 0;
}

B. Maximum Sum of Digits

◇题目传送门◆

题目大意

给定一个数 N N N,要求将这个数拆成 a + b a+b a+b的形式,使得 a , b a,b a,b两数每个数位上的数和最大。

思路

很明显的贪心题(注意千万不要看样例解释我就是这样被坑了)。

注意到若我们将一个数拆出的9越多,和越大。所以我们可以将 N N N拆成 99 … 9 99\ldots9 999的形式,就可以保证数位和最大。

正解代码

#include
#include
using namespace std;

long long N,a,b;

int S(long long x) {
	int ret=0;
	while(x) {
		ret+=(x%10);
		x/=10;
	}
	return ret;
}
int main() {
	#ifdef LOACL
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
	#endif
	scanf("%lld",&N);
	long long t=N/10;
	a=1;
	while(t) {
		a*=10;
		t/=10;
	}
	if(a%2==0)a--;
	b=N-a;
	printf("%d\n",S(a)+S(b));
	return 0;
}

C. Maximum Subrectangle

◇题目传送门◆

题目大意

给定两个长度分别为 N , M N,M N,M的两个数列 A , B A,B A,B及一个数 X X X,定义矩阵 C C C,其中 C i , j = A i × B j ( 1 ≤ i ≤ N , 1 ≤ j ≤ M ) C_{i,j}=A_i\times B_j(1\le i\le N,1\le j\le M) Ci,j=Ai×Bj(1iN,1jM),要求找出最大的矩形,使得 ∑ i = x 1 x 2 ∑ j = y 1 y 2 C i , j ≤ X \sum_{i=x_1}^{x_2}{\sum_{j=y_1}^{y_2}{C_{i,j}}}\le X i=x1x2j=y1y2Ci,jX

思路

如有读者想了解 O ( N 2 ) O(N^2) O(N2)算法请点这里

我们先来推一下式子:
∑ i = x 1 x 2 ∑ j = y 1 y 2 C i , j = ∑ i = x 1 x 2 ∑ j = y 1 y 2 ( A i × B j ) = ∑ i = x 1 x 2 A i × ∑ i = y 1 y 2 B j \sum_{i=x_1}^{x_2}{\sum_{j=y_1}^{y_2}{C_{i,j}}}=\sum_{i=x_1}^{x_2}{\sum_{j=y_1}^{y_2}{(A_i\times B_j)}}\\=\sum_{i=x_1}^{x_2}{A_i}\times\sum_{i=y_1}^{y_2}{B_j} i=x1x2j=y1y2Ci,j=i=x1x2j=y1y2(Ai×Bj)=i=x1x2Ai×i=y1y2Bj

不难发现这个题就转换为了在 A , B A,B A,B中分别选出一个长度最长的区间,使得他们的和的乘积不不超过 X X X

所以我们就可以先将 B B B序列中所有情况计算出来保存。再枚举 A A A的所有情况,并在已求得的数列上进行二分,就可以愉快地得出了答案。

然而。。我居然T了。。。

然后,我请了对面的大佬帮我调,他给了我一些调整:(以下两段引自Lucky_Glass的赛后总结)

可能出现有多个子串的元素之和相同的情况,我们默认对于两个元素之和相等的子串,取长度更大的那一个(实现时不需要这么做)。换句话说,我们通过限制元素之和求出长度(即限制子串的元素之和不超过某值,求出最大长度),这样的话很容易证明,随着限制的元素之和上升,子串的最大长度也应呈现出不降趋势。按理论,将每个子串按元素之和升序排列,最大长度也应该是不降序。

注意到我们这里只是将子串的元素之和存下来了,也就是只将元素之和与最大长度一一对应,并不是求不超过某元素之和的最大长度,所以很容易出现两个子串,一个的元素之和大于另一个,但长度却小于另一个。这是我打表发现的……如何解决?我们知道上述结论是正确的,那么在按元素之和排序后,再从头到尾遍历所有子串,并将第i个子串的长度强制与第i-1个子串的长度取 max ,这样对于一个子串变量,它所表示的就是不超过它的元素之和的子串的长度的最大值。这样就是真正的非降序了。

但是由于某些细节原因,我在考试的时候被一个别人hack别人的数据hack了。QAQ。。。

正解代码

#include
#include
#include
using namespace std;

typedef long long ll;
const int Maxn=2000;

ll N,M,X,A[Maxn+5],B[Maxn+5];
ll suma[Maxn+5],sumb[Maxn+5];
struct Node {
	ll sum;
	ll len;
	bool operator < (const Node rhs)const {
		return sum<rhs.sum;
	}
}S[Maxn*Maxn+5];

int main() {
	#ifdef LOACL
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
	#endif
	scanf("%d %d",&N,&M);
	for(int i=1;i<=N;i++) {
		scanf("%d",&A[i]);
		suma[i]=suma[i-1]+A[i];
	}
	for(int i=1;i<=M;i++) {
		scanf("%d",&B[i]);
		sumb[i]=sumb[i-1]+B[i];
	}
	scanf("%d",&X);
	int sz=0;
	for(int i=1;i<=M;i++)
		for(int j=i;j<=M;j++) {
			S[sz].len=j-i+1;
			S[sz].sum=sumb[j]-sumb[i-1];
			sz++;
		}
	sort(S,S+sz);
	for(int i=1;i<sz;i++)
		S[i].len=max(S[i-1].len,S[i].len);
	long long ans=0;
	for(int i=1;i<=N;i++)
		for(int j=i;j<=N;j++) {
			ll ts=suma[j]-suma[i-1];
			ll lim=X/ts;
			if(!lim) continue;
			ll lb=0,ub=sz-1;
			while(lb+1<ub) {
				ll mid=(lb+ub)/2;
				if(S[mid].sum<=lim)
					lb=mid;
				else ub=mid;
			}
			if(S[lb].sum>lim) continue;
			if(S[ub].sum<=lim) lb=ub;
			ans=max(ans,1LL*S[lb].len*(j-i+1));
		}
	printf("%lld\n",ans);
	return 0;
}

D. Social Circles

◇题目传送门◆

题目大意

N N N个人坐在一圈,每个人必须使他的左边空出 l i l_i li个椅子,右边空出 r i r_i ri个椅子。注意当只有一个人时,也必须满足这个要求。

思路

这题是考试时听另外的大佬讲的,AC非常愉快( ̄︶ ̄)↗ 。

大体思路如下:

首先我们要求总座位数最少,所以我们可以考虑使一个 l i l_i li尽量大的人和 r i r_i ri尽量大的人坐在一起。

s = ∑ i = 1 n l i + r i s=\sum_{i=1}^{n}{l_i+r_i} s=i=1nli+ri。假设有两个人 i , j i,j i,j j j j坐在 i i i的左边,则他们可以共用的空位有 min ⁡ ( l i , r j ) \min(l_i,r_j) min(li,rj),这样就会减少 min ⁡ ( l i , r j ) \min(l_i,r_j) min(li,rj)的位置。所以将 l i , r i l_i,r_i li,ri排序之后,分别用 s s s减掉对应位置上的值即可得出答案。

注意答案要加上 N N N(这些人也会占位置)。

备注:这个思路我暂时找不到正确性证明,如有能够证明此结论的,请在评论区留言。

正解代码

#include
#include
#include
using namespace std;

const int Maxn=1e5;

int N;
int A[Maxn+5],B[Maxn+5];
long long ans=0;

int main() {
	#ifdef LOACL
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
	#endif
	scanf("%d",&N);
	for(int i=1;i<=N;i++) {
		scanf("%d %d",&A[i],&B[i]);
		ans+=A[i]+B[i];
	}
	sort(A+1,A+N+1),sort(B+1,B+N+1);
	for(int i=1;i<=N;i++)
		ans-=min(A[i],B[i]);
	printf("%lld\n",ans+N);
	return 0;
}

你可能感兴趣的:(#,CodeForces)