[赛后总结]Codeforces Round #513(Div. 1 + Div. 2) A-D题解

总结

qwq
好弱呀…两场才勉强上蓝qwq…
这场比赛再次告诉我们——一定要看样例解释…否则很容易做自闭的.

A.Phone Numbers

00:05(0)
模拟
很不愉快的开始…
打完代码发现CodeBlocks和dev都崩溃了,很不愉快的再打了一遍。
看完题后发现只要长度和8的个数足够多就行。
因此输出min(n/11,cnt8)min(n/11,cnt_8)min(n/11,cnt8)即可。

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
#define MAXN 100
char s[MAXN+5];
int n,cnt,p;
int main()
{
    scanf("%d",&n);
    scanf("%s",s+1);
    p=n;
    for(int i=1;i<=n;i++)
        if(s[i]=='8')
        {
            if(p-11>=0){cnt++;p-=11;}//当时傻了...
        }
    printf("%d",cnt);
}

B.Maximum Sum of Digits

00:17(0)
贪心+结论
要求数SSS拆出来的两个数aaa,bbb各数位之和的最大值。
显然,我们要充分利用进位的性质。
Digit(x)Digit(x)Digit(x)表示数xxx有多少位。
a≥ba≥bab,若要进位,则一定满足Digit(S)=Digit(a)+1Digit(S)=Digit(a)+1Digit(S)=Digit(a)+1
考虑aaa应该如何取值。若要满足所有SSS都可以进位,显然aaa的每一位都应该为9
可以证明这是最优的(用反证法).
模拟并输出aaabbb各位数的和即可。
(代码有点麻烦)

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
#define MAXN 100
#define LL long long
LL n,N,D,A,B,k;
int main()
{
    scanf("%lld",&n);
    N=n;
    if(n<10){printf("%lld",n);return 0;}
    while(n)
    {
        n/=10;
        B+=9;
        k*=10;
        k+=9;
    }
    k/=10;B-=9;
    N-=k;
    while(N)
    {
		A+=N%10;
        N/=10;
    }
    printf("%lld",A+B);
}

C.Maximum Subrectangle

01:08(-2)
预处理
一道坑题…
考试的时候一眼二维滑窗,结果…
qwq
光速WA…后来发现只需要预处理就好了。

首先我们可以注意到的是对于矩阵(ax,ay),(bx,by)(a_x,a_y),(b_x,b_y)(ax,ay),(bx,by) (ax≤bx且ay≤by)(a_x≤b_x且a_y≤b_y)(axbxayby)
矩阵内的和为∑i=axbxAi∗∑j=aybyBj\sum_{i=a_x}^{b_x}{A_i}*\sum_{j=a_y}^{b_y}{B_j}i=axbxAij=aybyBj(可以展开之后看看)。
因此我们应该对AAA,BBB数组分别求前缀和。
考虑如何在AAA,BBB数组分别选取一个区间使其和的乘积小于xxx
注意我们求的是矩形面积最大,容易发现答案只要求了选取的两区间的长度。
我们可以贪心地想,对于两个数组,分别预处理长度为LLL时所取到的最小的区间和。
n2n^2n2枚举两数组区间长度,若≤x≤xx则计入答案即可。
注意要开long long
时间复杂度:O(n2)O(n^2)O(n2)

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
#define MAXN 2000
#define LL long long
#define INF 1000000000
LL Mina[MAXN+5],Minb[MAXN+5];
LL a[MAXN+5],b[MAXN+5];
LL Sa[MAXN+5],Sb[MAXN+5];
LL x,ans;
int n,m;
int main()
{
	scanf("%d%d",&n,&m);
	ans=0;
	for(int i=1;i<=n;i++)
	{scanf("%lld",&a[i]);Sa[i]=Sa[i-1]+a[i];}
	for(int i=1;i<=m;i++)
	{scanf("%lld",&b[i]);Sb[i]=Sb[i-1]+b[i];}
	scanf("%lld",&x);
	for(int L=1;L<=n;L++)
	{
		Mina[L]=INF;
		for(int i=L;i<=n;i++)
		Mina[L]=min(Mina[L],Sa[i]-Sa[i-L]);
	}
	for(int L=1;L<=m;L++)
	{
		Minb[L]=INF;
		for(int i=L;i<=m;i++)
		Minb[L]=min(Minb[L],Sb[i]-Sb[i-L]);
	}
	for(int A=1;A<=n;A++)
		for(int B=1;B<=m;B++)
		if(Mina[A]*Minb[B]<=x)ans=max(ans,(LL)A*B);
	printf("%lld",ans);
}

D.Social Circles

02:00(-1)
贪心+构造
本来以为C题已经够毒瘤了…结果这题更毒…
看完题以为只能用一个圈…打完代码之后才发现样例二都过不了…
再一次擦亮自己的狗眼…
如果可以围无限个圈的话,这题就是道水题了…

将问题抽象成一个合并问题,那么我们每次合并人iii,jjj的代价就是max(ri,lj)max(r_i,l_j)max(ri,lj)
那么我们的目标就是找到一种合并方式,使∑max(ri,lj)\sum max(r_i,l_j)max(ri,lj)的值最小。
注意到在所有lll,rrr中的最大值被算进最终答案的情况是无法改变的,那么我们的目标就是让比它略小的值不被算进最终答案里,也就是让最大值与与它接近的值合并。
现在我们来设计这个算法:
对于第一次操作,我们先将拥有两个数组最大值的人拿出来。
第二次,我们将次大的值拿出来。
若与上一个值的方向相反,则说明我们可以将这两个值合并起来,代价为max(ri,lj)max(r_i,l_j)max(ri,lj)
例如我们第一次拿出来的是lil_ili,第二次是rjr_jrj,方向相反,我们就可以合并它。
若方向不相反,则我们就将其拿出,另外构造一个环。
接下来不断进行如上操作…
然而这个算法未免有点麻烦…
考虑简化这个算法。
注意到最大的值总要等到最大的与它方向相反的值出现才能合并。
次大的值总要等到次大的与它方向相反的值出现才能合并。
以此类推。
那么我们只需要将lll,rrr数组排序,算∑max(ri,li)\sum max(r_i,l_i)max(ri,li)即可。
注意要开long long
时间复杂度:O(nlogn)O(nlogn)O(nlogn)
(好像讲的很不清楚,但我已经尽力了啊qwq…)

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
#define MAXN 100000
#define LL long long
int n;
int L[MAXN+5],R[MAXN+5];
LL ans;
bool cmp(int s1,int s2)
{
	return s1>s2;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d%d",&L[i],&R[i]);
	sort(L+1,L+n+1,cmp);
	sort(R+1,R+n+1,cmp);
	for(int i=1;i<=n;i++)
	ans+=max(L[i],R[i]);
	printf("%lld",ans+n);
}

E.Sergey and Subway

QwQ
体验感极差…
前面用了太长的时间,再次凉在E题…
[题解明天补]
注意要开long long

转载于:https://www.cnblogs.com/Panda-hu/p/11145753.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值