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<cstdio>
#include<algorithm>
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 99…9的形式,就可以保证数位和最大。
正解代码
#include<cstdio>
#include<algorithm>
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(1≤i≤N,1≤j≤M),要求找出最大的矩形,使得 ∑ 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=x1∑x2j=y1∑y2Ci,j≤X
思路
如有读者想了解 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=x1∑x2j=y1∑y2Ci,j=i=x1∑x2j=y1∑y2(Ai×Bj)=i=x1∑x2Ai×i=y1∑y2Bj
不难发现这个题就转换为了在 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<vector>
#include<cstdio>
#include<algorithm>
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<cstdio>
#include<cstring>
#include<algorithm>
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;
}