AtCoder Beginner Contest 203 (Sponsored by Panasonic) A~E 题解

A - Chinchirorin

题目大意

给定三个整数 a , b , c a,b,c a,b,c,如果它们中有两个相等,输出另一个;否则,输出 0 0 0

1 ≤ a , b , c ≤ 6 1\le a,b,c\le 6 1a,b,c6

输入格式

a   b   c a~b~c a b c

输出格式

如果 a , b , c a,b,c a,b,c中有两个相等,输出另一个;否则,输出 0 0 0

样例

a a a b b b c c c输出
2 2 2 5 5 5 2 2 2 5 5 5
4 4 4 5 5 5 6 6 6 0 0 0
1 1 1 1 1 1 1 1 1 1 1 1

分析

A A A题还是一如既往的水……直接暴力判断三种相等的情况即可。

代码

#include <cstdio>
using namespace std;

int main()
{
	int a, b, c;
	scanf("%d%d%d", &a, &b, &c);
	if(a == b) printf("%d\n", c);
	else if(b == c) printf("%d\n", a);
	else if(a == c) printf("%d\n", b);
	else puts("0");
	return 0;
}

B - AtCoder Condominium

题目大意

给定 N N N K K K,求 ∑ i = 1 N ∑ j = 1 K i 0 j ‾ \sum\limits_{i=1}^N\sum\limits_{j=1}^K \overline{i0j} i=1Nj=1Ki0j
1 ≤ N , K ≤ 9 1\le N,K\le 9 1N,K9

输入格式

N   K N~K N K

输出格式

输出 ∑ i = 1 N ∑ j = 1 K i 0 j ‾ \sum\limits_{i=1}^N\sum\limits_{j=1}^K \overline{i0j} i=1Nj=1Ki0j

样例

N N N K K K输出
1 1 1 2 2 2 203 203 203
3 3 3 3 3 3 1818 1818 1818

分析

本题可以直接暴力,但我使用的是如下 O ( 1 ) \mathcal O(1) O(1)算法:
根据 i 0 j ‾ = 100 i + j \overline{i0j}=100i+j i0j=100i+j 1 + 2 + ⋯ + N = N ( N + 1 ) 2 1+2+\dots+N=\frac{N(N+1)}2 1+2++N=2N(N+1),则有如下推导:

  • ∑ i = 1 N ∑ j = 1 K i 0 j ‾ = ∑ i = 1 N ∑ j = 1 K 100 i + ∑ i = 1 N ∑ j = 1 K j \sum\limits_{i=1}^N\sum\limits_{j=1}^K \overline{i0j}=\sum\limits_{i=1}^N\sum\limits_{j=1}^K 100i+\sum\limits_{i=1}^N\sum\limits_{j=1}^K j i=1Nj=1Ki0j=i=1Nj=1K100i+i=1Nj=1Kj
  • ∑ i = 1 N ∑ j = 1 K 100 i = ∑ i = 1 N 100 i K = 100 K ∑ i = 1 N i = 100 N ( N + 1 ) K 2 \sum\limits_{i=1}^N\sum\limits_{j=1}^K 100i=\sum\limits_{i=1}^N 100iK=100K\sum\limits_{i=1}^N i=\frac{100N(N+1)K}2 i=1Nj=1K100i=i=1N100iK=100Ki=1Ni=2100N(N+1)K
  • ∑ i = 1 N ∑ j = 1 K j = ∑ i = 1 N K ( K + 1 ) 2 = K ( K + 1 ) N 2 \sum\limits_{i=1}^N\sum\limits_{j=1}^K j=\sum\limits_{i=1}^N\frac{K(K+1)}2=\frac{K(K+1)N}2 i=1Nj=1Kj=i=1N2K(K+1)=2K(K+1)N
  • ∑ i = 1 N ∑ j = 1 K i 0 j ‾ = 100 N ( N + 1 ) K 2 + K ( K + 1 ) N 2 = 100 N ( N + 1 ) K + K ( K + 1 ) N 2 \sum\limits_{i=1}^N\sum\limits_{j=1}^K \overline{i0j}=\frac{100N(N+1)K}2+\frac{K(K+1)N}2=\frac{100N(N+1)K+K(K+1)N}2 i=1Nj=1Ki0j=2100N(N+1)K+2K(K+1)N=2100N(N+1)K+K(K+1)N

这样,我们就可以直接通过公式 100 N ( N + 1 ) K + K ( K + 1 ) N 2 \frac{100N(N+1)K+K(K+1)N}2 2100N(N+1)K+K(K+1)N计算出结果了。

代码

#include <cstdio>
using namespace std;

inline int sum(int x) { return x * (x + 1) >> 1; }

int main()
{
	int n, k;
	scanf("%d%d", &n, &k);
	printf("%d\n", sum(n) * k * 100 + sum(k) * n);
	return 0;
}

C - Friends and Travel costs

题目大意

1 0 100 + 1 10^{100}+1 10100+1个村庄,分别为村庄 0 , 1 , … , 1 0 100 0,1,\dots,10^{100} 0,1,,10100,相邻两个村庄之间的过路费是 1 1 1元。
Taro一开始有 K K K元且在村庄 0 0 0。他想要到达编号尽可能大的村庄。
他有 N N N个朋友。第 i i i个朋友会在Taro到达村庄 A i A_i Ai时给他 B i B_i Bi元。
求Taro最后到达的村庄的编号。

1 ≤ N ≤ 2 × 1 0 5 1\le N\le 2\times10^5 1N2×105
1 ≤ K ≤ 1 0 9 1\le K\le 10^9 1K109
1 ≤ A i ≤ 1 0 18 1\le A_i\le 10^{18} 1Ai1018
1 ≤ B i ≤ 1 0 9 1\le B_i\le 10^9 1Bi109

输入格式

N   K N~K N K
A 1   B 1 A_1~B_1 A1 B1
A 2   B 2 A_2~B_2 A2 B2
… \dots
A N   B N A_N~B_N AN BN

输出

输出Taro最后到达的村庄的编号。

样例

样例输入1

2 3
2 1
5 10

样例输出1

4

样例输入2

5 1000000000
1 1000000000
2 1000000000
3 1000000000
4 1000000000
5 1000000000

样例输出2

6000000000

请不要使用 32 32 32位整数。

样例输入3

3 2
5 5
2 1
2 2

样例输出3

10

Taro在一个村庄可能有多个朋友。

分析

根据题目中的数据范围,我们可以证明答案严格小于 2 64 2^{64} 264,所以我们使用unsigned long long作为存储数据类型。
可是,由于村庄数量还是太多,我们仍然无法依次模拟到达每个村庄。
我们发现 N N N较小,所以我们可以从朋友的角度考虑。
我们可以按 A i A_i Ai排序所有朋友( B i B_i Bi的顺序不重要),这样就能把整个行程形成分成若干个区间,并依次判断每个区间是否能走完即可。

代码

注意:我这里排序使用的是优先队列(priority_queue

#include <cstdio>
#include <queue>
#define maxn 200005
#define INF 18446744073709551615ULL
using namespace std;

using ULL = unsigned long long;
using pll = pair<ULL, ULL>;

int main()
{
	int n;
	ULL k;
	scanf("%d%llu", &n, &k);
	priority_queue<pll, vector<pll>, greater<pll> > q;
	for(int i=0; i<n; i++)
	{
		ULL a, b;
		scanf("%llu%llu", &a, &b);
		q.emplace(a, b);
	}
	ULL lastv = 0ULL;
	q.emplace(INF, 0ULL);
	while(!q.empty())
	{
		auto [a, b] = q.top(); q.pop();
		ULL cost = a - lastv;
		if(k < cost)
		{
			printf("%llu\n", lastv + k);
			return 0;
		}
		k -= cost;
		lastv = a, k += b;
	}
	return 0;
}

D - Pond

题目大意

给定一个 N × N N\times N N×N的正方形矩阵 A A A,第 i i i行第 j j j列的元素是 A i , j A_{i,j} Ai,j
A A A中所有的 K × K K\times K K×K的子矩阵的中间值的最小值。
一个 K × K K\times K K×K的正方形的中间值为其中第 ( ⌊ K 2 2 ⌋ + 1 ) (\left\lfloor\frac{K^2}2\right\rfloor+1) (2K2+1)大的值。

1 ≤ K ≤ N ≤ 800 1\le K\le N\le 800 1KN800
1 ≤ A i , j ≤ 1 0 9 1\le A_{i,j}\le 10^9 1Ai,j109

如果不能理解题意,请看下图:
在这里插入图片描述

对应的输入输出:

3 2
5 9 8
2 1 3
7 4 6

/

2

输入格式

N   K N~K N K
A 1 , 1   A 1 , 2   …   A 1 , N A_{1,1}~A_{1,2}~\dots~A_{1,N} A1,1 A1,2  A1,N
A 2 , 1   A 2 , 2   …   A 2 , N A_{2,1}~A_{2,2}~\dots~A_{2,N} A2,1 A2,2  A2,N
A N , 1   A N , 2   …   A N , N A_{N,1}~A_{N,2}~\dots~A_{N,N} AN,1 AN,2  AN,N

输出格式

输出答案。

样例

样例输入1

3 2
1 7 0
5 8 11
10 4 2

样例输出1

4

N = 3       K = 2 A = [ 1 7 0 5 8 11 10 4 2 ] N=3~~~~~K=2\\ A=\begin{bmatrix} 1 & 7 & 0\\ 5 & 8 & 11\\ 10 & 4 & 2 \end{bmatrix} N=3     K=2A= 15107840112
我们有四个 2 × 2 2\times2 2×2的正方形: { 8 , 7 , 5 , 1 } ,   { 11 , 8 , 7 , 0 } ,   { 10 , 8 , 5 , 4 } ,   { 11 , 8 , 4 , 2 } \{8, 7, 5, 1\}, ~\{11,8,7,0\},~ \{10,8,5,4\}, ~\{11,8,4,2\} {8,7,5,1}, {11,8,7,0}, {10,8,5,4}, {11,8,4,2}
我们依次从每个的元素中取第 ⌊ K 2 2 ⌋ + 1 = 3 \left\lfloor\frac{K^2}2\right\rfloor+1=3 2K2+1=3大的: { 5 , 7 , 5 , 4 } \{5,7,5,4\} {5,7,5,4}
最后,我们从 { 5 , 7 , 5 , 4 } \{5,7,5,4\} {5,7,5,4}中选出最小的: 4 4 4

样例输入2

3 3
1 2 3
4 5 6
7 8 9

样例输出2

5

分析

本题可以二分答案。我们判定一个数是否为一个 K × K K\times K K×K的正方形的中间值时,只需要计算这个正方形内严格大于这个数的数的个数是否为 ⌊ K 2 2 ⌋ \left\lfloor\frac{K^2}2\right\rfloor 2K2即可。
因此,我们可以使用矩阵前缀和快速计算一个正方形内严格大于一个数的数的数的个数。
总时间复杂度 O ( n 2 log ⁡ max ⁡ { A } ) \mathcal O(n^2\log\max\{A\}) O(n2logmax{A})

代码

#include <cstdio>
#define maxn 805
#define INF 2147483647
using namespace std;

int a[maxn][maxn], dp[maxn][maxn], n, k, target;

inline int count(int x1, int y1, int x2, int y2)
{
	return dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1];
}

inline bool check(int x)
{
	for(int i=1; i<=n; i++)
		for(int j=1; j<=n; j++)
			dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + (a[i][j] > x);
	for(int x2=k; x2<=n; x2++)
		for(int y2=k; y2<=n; y2++)
		{
			int x1 = x2 - k + 1, y1 = y2 - k + 1;
			if(count(x1, y1, x2, y2) < target)
				return true;
		}
	return false;
}

int main()
{
	scanf("%d%d", &n, &k);
	target = (k * k >> 1) + 1;
	int l = INF, r = 0, ans = 0;
	for(int i=1; i<=n; i++)
		for(int j=1; j<=n; j++)
		{
			scanf("%d", a[i] + j);
			if(a[i][j] > r) r = a[i][j];
			if(a[i][j] < l) l = a[i][j];
		}
	while(l <= r)
	{
		int mid = l + r >> 1;
		if(check(mid)) ans = mid, r = mid - 1;
		else l = mid + 1;
	}
	printf("%d\n", ans);
	return 0;
}

E - White Pawn

题目大意

有一个 ( 2 N + 1 ) × ( 2 N + 1 ) (2N+1)\times(2N+1) (2N+1)×(2N+1)的正方形棋盘,行数、列数下标都依次从 0 0 0 2 N 2N 2N。我们用 ( i , j ) (i,j) (i,j)表示棋盘上 i i i j j j列的位置。
我们有一颗棋子,初始位置在 ( 0 , N ) (0,N) (0,N)。棋盘上有 M M M个黑方格,第 i i i个的位置在 ( X i , Y i ) (X_i,Y_i) (Xi,Yi),其余都是白方格。
当棋子在 ( i , j ) (i,j) (i,j)时,你可以执行任意下列操作,但不能移出棋盘:

  • ( i + 1 , j ) (i+1,j) (i+1,j)是白色时,移到 ( i + 1 , j ) (i+1,j) (i+1,j)
  • ( i + 1 , j − 1 ) (i+1,j-1) (i+1,j1)是黑色时,移到 ( i + 1 , j − 1 ) (i+1,j-1) (i+1,j1)
  • ( i + 1 , j + 1 ) (i+1,j+1) (i+1,j+1)是黑色是,移到 ( i + 1 , j + 1 ) (i+1,j+1) (i+1,j+1)

棋盘上的方格不能移动。求棋盘的最后一行的能到达的列的个数。

1 ≤ N ≤ 1 0 9 1\le N\le 10^9 1N109
0 ≤ M ≤ 2 × 1 0 5 0\le M\le 2\times 10^5 0M2×105
1 ≤ X i ≤ 2 N 1\le X_i\le 2N 1Xi2N
0 ≤ Y i ≤ 2 N 0\le Y_i\le 2N 0Yi2N
( X i , Y i ) (X_i,Y_i) (Xi,Yi)互不相等。

输入格式

N   M N~M N M
X 1   Y 1 X_1~Y_1 X1 Y1
X 2   Y 2 X_2~Y_2 X2 Y2
⋮ \vdots
X M   Y M X_M~Y_M XM YM

输出格式

输出棋盘的最后一行的能到达的列的个数。

样例

样例输入1

2 4
1 1
1 2
2 0
4 2

样例输出1

3

我们可以将棋子移动到 ( 4 , 0 ) (4,0) (4,0) ( 4 , 1 ) (4,1) (4,1) ( 4 , 2 ) (4,2) (4,2),如下:

  • ( 0 , 2 ) → ( 1 , 1 ) → ( 2 , 1 ) → ( 3 , 1 ) → ( 4 , 2 ) (0,2)\to(1,1)\to(2,1)\to(3,1)\to(4,2) (0,2)(1,1)(2,1)(3,1)(4,2)
  • ( 0 , 2 ) → ( 1 , 1 ) → ( 2 , 1 ) → ( 3 , 1 ) → ( 4 , 1 ) (0,2)\to(1,1)\to(2,1)\to(3,1)\to(4,1) (0,2)(1,1)(2,1)(3,1)(4,1)
  • ( 0 , 2 ) → ( 1 , 1 ) → ( 2 , 0 ) → ( 3 , 0 ) → ( 4 , 0 ) (0,2)\to(1,1)\to(2,0)\to(3,0)\to(4,0) (0,2)(1,1)(2,0)(3,0)(4,0)

我们不能移动到 ( 4 , 3 ) (4,3) (4,3) ( 4 , 4 ) (4,4) (4,4),所以输出 3 3 3

样例输入2

1 1
1 1

样例输出2

0

我们无法移动棋子。

分析

我们发现,当 N N N较大时,大多数行多是空着的,所以我们从每个 X i X_i Xi开始考虑。对于白色的位置 ( i , j ) (i,j) (i,j),如果不能到达 ( i − 1 , j ) (i-1,j) (i1,j),则不能到达 ( i , j ) (i,j) (i,j)。相反,对于黑色的 ( i , j ) (i,j) (i,j),如果能到达 ( i − 1 , j − 1 ) (i-1,j-1) (i1,j1) ( i − 1 , j + 1 ) (i-1,j+1) (i1,j+1),则能到达 ( i , j ) (i,j) (i,j)
因此,我们先排序每个 ( X i , Y i ) (X_i,Y_i) (Xi,Yi),再对于每个有黑色的行,用set维护能到达的列数,再按上述方法判断即可。

代码

#include <cstdio>
#include <set>
#include <vector>
#include <algorithm>
#pragma GCC optimize("Ofast")
using namespace std;

int main()
{
	int n, m;
	scanf("%d%d", &n, &m);
	vector<pair<int, int> > black;
	black.reserve(m);
	while(m--)
	{
		int x, y;
		scanf("%d%d", &x, &y);
		black.emplace_back(x, y);
	}
	m = black.size();
	sort(black.begin(), black.end());
	set<int> cols;
	cols.insert(n);
	for(int l=0, r=0; l<m; l=r)
	{
		while(r < m && black[r].first == black[l].first) r ++;
		vector<int> rem, add;
		for(int i=l; i<r; i++)
		{
			int y = black[i].second;
			bool ok = cols.count(y - 1) || cols.count(y + 1);
			if(cols.count(y))
			{
				if(!ok)
					rem.push_back(y);
			}
			else if(ok)
				add.push_back(y);
		}
		for(int y: rem) cols.erase(y);
		for(int y: add) cols.insert(y);
	}
	printf("%llu\n", cols.size());
	return 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、付费专栏及课程。

余额充值