AtCoder Beginner Contest 353 (ABC353) A-D 题解

我要是下次打ABC还不睡午觉就是↑↓

前排提示: D D D题经典再现

在这里插入图片描述

比赛链接

A . A. A.

传送门

题意:在这里插入图片描述
c o d e : code: code:

#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
const int N = 105;
int n,m,h,w;
int a[N];
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		if(i>=2)
		{
			if(a[i]>a[1])
			{
				printf("%d",i);
				return 0;
			}
		}
	}
	printf("-1");
	return 0;
}

B . B. B.

传送门

题意:一个景点容量为 K K K个人,一共 N N N组人,每组人数 A i A_i Ai。现在让这 N N N组人一组一组去景点,如果已进入景点的总人数加上 A i Ai Ai个人后小于 K K K,那就让 A i A_i Ai个人加入景点中;如果已进入总人数加上 A i A_i Ai个人后大于 K K K,那就让“景点启动次数”加一,总人数清零,再让这 A i A_i Ai个人去景点,直到 N N N组人全部看完。注意第 N N N组进去后也要让总人数清零,“景点启动次数”加一。求景点启动总次数。

在这里插入图片描述

数据规模: N , K , A i < = 100 N,K,Ai<=100 N,K,Ai<=100

c o d e : code: code:

#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
const int N = 105;
int n,m,h,w,k;
int a[N];
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	int cnt=0;//当前景点中总的人数
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		if(cnt+a[i]<=k)
		{
			cnt+=a[i];
		}
		else
		{
			cnt=a[i];
			ans++;
		}
	}
	printf("%d",ans+1);
	return 0;
}


C . C. C.

传送门

题意:

在这里插入图片描述

数据规模: N < = 3 e 5 , A i < = 1 e 8 N<=3e5,A_i<=1e8 N<=3e5,Ai<=1e8

做法:
先看数据规模, x , y < = 1 0 8 x,y<=10^8 x,y<=108保证了 ( x + y ) (x+y) (x+y) 1 0 8 10^8 108取模的时候最多减一次 1 0 8 10^8 108或者一次也不减,换句话说, f ( x , y ) = { x + y x + y < 1 0 8 x + y − 1 0 8 x + y > = 1 0 8 f(x,y)=\begin{cases} x+y & x+y<10^8 \\ x+y-10^8 & x+y>=10^8\\ \end{cases} f(x,y)={x+yx+y108x+y<108x+y>=108
那么我们只需要先求 Σ i = 1 N − 1 Σ j = i + 1 N A i + A j \Sigma_{i=1}^{N-1} \Sigma_{j=i+1}^{N} A_i+A_j Σi=1N1Σj=i+1NAi+Aj的值,再用这个值减去 n u m ∗ 1 0 8 num*10^8 num108就是答案,其中 n u m num num表示在所有循环到的 f ( A i , A j ) f(A_i,A_j) f(Ai,Aj) A i + A j > = 1 0 8 A_i+A_j>=10^8 Ai+Aj>=108的出现次数

分析一下这个式子,发现交换 A i A_i Ai的顺序对答案并不会产生影响,所以我们可以先升序排序。

再开双指针,一个 i i i来枚举 A i A_i Ai,另一个枚举位置 j j j,使得 A j A_j Aj及以后的 A k A_k Ak都能让 ( A i + A k ) > = 1 0 8 (A_i+A_k)>=10^8 (Ai+Ak)>=108,这样每个 A i Ai Ai n u m num num的贡献次数就是 ( n − j + 1 ) (n-j+1) (nj+1),特别注意,如果 j j j i i i的前面,意味着 A i A_i Ai之后的任何 A x A_x Ax都使得 ( A i + A x ) > = 1 0 8 (A_i+A_x)>=10^8 (Ai+Ax)>=108成立, A i A_i Ai贡献次数为 ( n − ( i + 1 ) + 1 ) (n-(i+1)+1) (n(i+1)+1),再注意,如果找不到 j j j,那么 A i A_i Ai贡献次数为 0 0 0
(大概讲清楚了吧,不过赛时代码写的比较乱)

c o d e : code: code:

#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <string>
#define int long long
using namespace std;
const int N = 3e5+5;
const int mod = 1e8;
int n,m,h,w;
int a[N];
int b[N];
int sum[N];
int ans;
signed main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i];
	ans=sum[n]*(n-1);
	sort(a+1,a+1+n);
	int head;
	int flag=0;
	for(int i=1;i<=n;i++)
	{
		if(a[i]+a[n]>=mod)
		{
			head=i;
			flag=1;
			break;
		}		
	}
	if(!flag)//任意两个A相加都比1e8小
	{
		printf("%lld",ans);
		return 0;
	}
	int tail=n;
	for(int i=head;i<=n;i++)
	{
		while(1)
		{
			if(a[i]+a[tail]>=mod) tail--;
			else break;
		}
		tail++;
		b[i]=tail;//b[i]代表第二个指针的位置,从b[i]往后的j都满足A_i+A_j>=1e8
	}
	for(int i=head;i<=n;i++)
	{
		if(b[i]<=i)
		{
			ans-=(n-i)*mod; continue;
		}
		ans-=(n-b[i]+1)*mod;
	}
	printf("%lld",ans);
	return 0;
}


D . D. D.

传送门

题意:在这里插入图片描述

数据规模: N < = 2 ∗ 1 0 5 , A i < = 1 0 9 N<=2*10^5,A_i<=10^9 N<=2105,Ai<=109

我感觉这个题比C还好想呢,不过我连WA七次也是挺逆天的

分析:

易知 f ( x , y ) = x ∗ 1 0 d i g i t ( y ) + y f(x,y)=x*10^{digit(y)}+y f(x,y)=x10digit(y)+y ,其中 d i g i t ( y ) digit(y) digit(y)表示y的位数。

【下文把 f f f前面那个数称作 x x x位置,,后边那个数称为 y y y位置,形如 f ( x , y ) f(x,y) f(x,y)

考虑每一个 A i A_i Ai对答案的总贡献。由两部分组成,一部分是在 y y y位置的贡献,另一部分是在 x x x位置的贡献。

y y y位置贡献:在这一坨求和的过程中, A i A_i Ai出现在 y y y位置的次数如果是 s u m y sumy sumy,那么 A i A_i Ai y y y位置的所有贡献为 s u m y ∗ A i sumy*A_i sumyAi。一眼丁真, s u m y = ( i − 1 ) sumy=(i-1) sumy=(i1),看不出来可以把求和号拆开找规律。

x x x位置贡献: A i A_i Ai后边的每一个 A j A_j Aj都和 A i A_i Ai拼接且仅拼接了一次,那么显然

x 位置总贡献 = Σ j = i + 1 N A i ∗ 1 0 A j = A i ∗ Σ j = i + 1 N 1 0 A j x位置总贡献 = \Sigma_{j=i+1}^{N} A_i*10^{A_j} = A_i*\Sigma_{j=i+1}^{N} 10^{A_j} x位置总贡献=Σj=i+1NAi10Aj=AiΣj=i+1N10Aj

后边这个东西( Σ j = i + 1 N 1 0 A j \Sigma_{j=i+1}^{N} 10^{A_j} Σj=i+1N10Aj)是个后缀和,这个题就做完了,接下来只需要把每个 A i A_i Ai x x x y y y位置的总贡献加起来就是答案。

c o d e : code: code:

#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <string>
#define int long long
using namespace std;
const int N = 2e5+5;
const int mod = 998244353;
int n,m,h,w;
int a[N];
int suf[N];
int p[15];
int dit(int x)
{
	int ans=0;
	while(x)
	{
		x/=10;
		ans++;
	}
	return ans;
}
//void check()
signed main()
{
	scanf("%lld",&n);
	p[1]=10;
	for(int i=2;i<=10;i++) p[i]=p[i-1]*10;//预处理指数
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	int ans=0;
	for(int i=n;i>=1;i--)
	{
		suf[i]=suf[i+1];
		int nowdit=dit(a[i]);
		suf[i]+=p[nowdit];
		suf[i]%=mod;
		ans+=suf[i+1]*a[i]%mod;
		ans%=mod;
	}
	for(int i=1;i<=n;i++) ans+=a[i]*(i-1),ans%=mod;
	printf("%lld",ans%mod);
	return 0;
}


  • 39
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AtCoder Beginner Contest 134 是一场 AtCoder 的入门级比赛,以下是每道题的简要题解: A - Dodecagon 题目描述:已知一个正十二边形的边长,求它的面积。 解题思路:正十二边形的内角为 $150^\circ$,因此可以将正十二边形拆分为 12 个等腰三角形,通过三角形面积公式计算面积即可。 B - Golden Apple 题目描述:有 $N$ 个苹果和 $D$ 个盘子,每个盘子最多可以装下 $2D+1$ 个苹果,求最少需要多少个盘子才能装下所有的苹果。 解题思路:每个盘子最多可以装下 $2D+1$ 个苹果,因此可以将苹果平均分配到每个盘子中,可以得到最少需要 $\lceil \frac{N}{2D+1} \rceil$ 个盘子。 C - Exception Handling 题目描述:给定一个长度为 $N$ 的整数序列 $a$,求除了第 $i$ 个数以外的最大值。 解题思路:可以使用两个变量 $m_1$ 和 $m_2$ 分别记录最大值和次大值。遍历整个序列,当当前数不是第 $i$ 个数时,更新最大值和次大值。因此,最后的结果应该是 $m_1$ 或 $m_2$ 中较小的一个。 D - Preparing Boxes 题目描述:有 $N$ 个盒子和 $M$ 个物品,第 $i$ 个盒子可以放入 $a_i$ 个物品,每个物品只能放在一个盒子中。现在需要将所有的物品放入盒子中,每次操作可以将一个盒子内的物品全部取出并分配到其他盒子中,求最少需要多少次操作才能完成任务。 解题思路:首先可以计算出所有盒子中物品的总数 $S$,然后判断是否存在一个盒子的物品数量大于 $\lceil \frac{S}{2} \rceil$,如果存在,则无法完成任务。否则,可以用贪心的思想,每次从物品数量最多的盒子中取出一个物品,放入物品数量最少的盒子中。因为每次操作都会使得物品数量最多的盒子的物品数量减少,而物品数量最少的盒子的物品数量不变或增加,因此这种贪心策略可以保证最少需要的操作次数最小。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值