ABC203 A~E
- [A - Chinchirorin](https://atcoder.jp/contests/abc203/tasks/abc203_a)
- [B - AtCoder Condominium](https://atcoder.jp/contests/abc203/tasks/abc203_b)
- [C - Friends and Travel costs](https://atcoder.jp/contests/abc203/tasks/abc203_c)
- [D - Pond](https://atcoder.jp/contests/abc203/tasks/abc203_d)
- [E - White Pawn](https://atcoder.jp/contests/abc203/tasks/abc203_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 1≤a,b,c≤6
输入格式
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=1∑Nj=1∑Ki0j。
1
≤
N
,
K
≤
9
1\le N,K\le 9
1≤N,K≤9
输入格式
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=1∑Nj=1∑Ki0j。
样例
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=1∑Nj=1∑Ki0j=i=1∑Nj=1∑K100i+i=1∑Nj=1∑Kj
- ∑ 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=1∑Nj=1∑K100i=i=1∑N100iK=100Ki=1∑Ni=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=1∑Nj=1∑Kj=i=1∑N2K(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=1∑Nj=1∑Ki0j=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
1≤N≤2×105
1
≤
K
≤
1
0
9
1\le K\le 10^9
1≤K≤109
1
≤
A
i
≤
1
0
18
1\le A_i\le 10^{18}
1≤Ai≤1018
1
≤
B
i
≤
1
0
9
1\le B_i\le 10^9
1≤Bi≤109
输入格式
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
1≤K≤N≤800
1
≤
A
i
,
j
≤
1
0
9
1\le A_{i,j}\le 10^9
1≤Ai,j≤109
如果不能理解题意,请看下图:
对应的输入输出:
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,j−1)是黑色时,移到 ( i + 1 , j − 1 ) (i+1,j-1) (i+1,j−1)。
- ( 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
1≤N≤109
0
≤
M
≤
2
×
1
0
5
0\le M\le 2\times 10^5
0≤M≤2×105
1
≤
X
i
≤
2
N
1\le X_i\le 2N
1≤Xi≤2N
0
≤
Y
i
≤
2
N
0\le Y_i\le 2N
0≤Yi≤2N
(
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)
(i−1,j),则不能到达
(
i
,
j
)
(i,j)
(i,j)。相反,对于黑色的
(
i
,
j
)
(i,j)
(i,j),如果能到达
(
i
−
1
,
j
−
1
)
(i-1,j-1)
(i−1,j−1)或
(
i
−
1
,
j
+
1
)
(i-1,j+1)
(i−1,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;
}