ABC198 A~E
- [A - Div](https://atcoder.jp/contests/abc198/tasks/abc198_a)
- [B - Palindrome with leading zeros](https://atcoder.jp/contests/abc198/tasks/abc198_b)
- [C - Compass Walking](https://atcoder.jp/contests/abc198/tasks/abc198_c)
- [D - Send More Money](https://atcoder.jp/contests/abc198/tasks/abc198_d)
- [E - Unique Color](https://atcoder.jp/contests/abc198/tasks/abc198_e)
A - Div
题目大意
两个男孩要分 N N N颗糖。问一共有几种分法(每个男孩都必须分到糖)?
1 ≤ N ≤ 15 1\le N\le 15 1≤N≤15
输入格式
N N N
输出格式
将答案输出为一个整数。
样例
N N N | 输出 |
---|---|
2 2 2 | 1 1 1 |
1 1 1 | 0 0 0 |
3 3 3 | 2 2 2 |
分析
这题说白了就是将 N N N分成 A A A和 B B B两个正整数的和( A + B A+B A+B和 B + A B+A B+A是两种分法),所以列表如下:
A A A | B B B |
---|---|
1 1 1 | N − 1 N-1 N−1 |
2 2 2 | N − 2 N-2 N−2 |
… \dots … | … \dots … |
N − 1 N-1 N−1 | 1 1 1 |
这个表一共有 N − 1 N-1 N−1项,所以我们直接输出 N − 1 N-1 N−1即可。 |
代码
#include <cstdio>
using namespace std;
int main()
{
int n;
scanf("%d", &n);
printf("%d\n", n - 1);
return 0;
}
B - Palindrome with leading zeros
题目大意
给定一个整数
N
N
N。
是否可以在
N
N
N的十进制表示的前面填上任意(可以为
0
0
0)个0
,使得
N
N
N变成一个回文数?
0 ≤ N ≤ 1 0 9 0\le N\le 10^9 0≤N≤109
输入格式
N N N
输出格式
输出Yes
或者No
。
样例
N N N | 输出 |
---|---|
1210 1210 1210 | Yes |
777 777 777 | Yes |
123456789 123456789 123456789 | No |
分析
如果能在
N
N
N的前面加上一些0
使得
N
N
N变成一个回文数,那么将
N
N
N去掉末尾的所有0
后也一定能得到一个回文数。所以,我们只需将
N
N
N末尾的0
去掉后,再判断它是不是一个回文数即可。
代码
#include <cstdio>
using namespace std;
char s[10];
int main()
{
char c;
int len = 0;
while((c = getchar()) != '\n')
s[len++] = c;
while(len > 0 && s[--len] == '0');
for(int i=0; i<=len; i++)
if(s[i] != s[len - i])
{
puts("No");
return 0;
}
puts("Yes");
return 0;
}
C - Compass Walking
题目大意
在一个二维的平面上,一个人每一步都只能走正好
R
R
R个距离单位。
这个人从
(
0
,
0
)
(0,0)
(0,0)走到
(
X
,
Y
)
(X,Y)
(X,Y)至少需要多少步?
注意:在二维平面上,两个点
(
x
1
,
y
1
)
(x_1, y_1)
(x1,y1)和
(
x
2
,
y
2
)
(x_2,y_2)
(x2,y2)的距离是
(
x
1
−
x
2
)
2
+
(
y
1
−
y
2
)
2
\sqrt{(x_1-x_2)^2+(y_1-y_2)^2}
(x1−x2)2+(y1−y2)2。
1
≤
R
≤
1
0
5
1\le R\le 10^5
1≤R≤105
0
≤
X
,
Y
≤
1
0
5
0\le X,Y\le 10^5
0≤X,Y≤105
(
X
,
Y
)
≠
(
0
,
0
)
(X,Y)\ne(0,0)
(X,Y)=(0,0)
输入格式
R X Y R~X~Y R X Y
输出格式
输出一行,即最少需要的步数。
样例
R R R | X X X | Y Y Y | 输出 |
---|---|---|---|
5 5 5 | 15 15 15 | 0 0 0 | 3 3 3 |
5 5 5 | 11 11 11 | 0 0 0 | 3 3 3 |
3 3 3 | 4 4 4 | 4 4 4 | 2 2 2 |
分析
我们可以先定义 d = X 2 + Y 2 d=\sqrt{X^2+Y^2} d=X2+Y2,即 ( X , Y ) (X,Y) (X,Y)到 ( 0 , 0 ) (0,0) (0,0)的距离,则有如下解法:
- 如果 d = R d=R d=R,则总共只需要 1 1 1步。
- 如果 d < R d<R d<R,则总共需要 2 2 2步。
- 如果 d > R d>R d>R,总共需要 ⌈ d r ⌉ \lceil\frac d r\rceil ⌈rd⌉步。
这样,我们就可以写代码了。
代码
这题很容易出现精度问题,但我用long double
居然卡过去了……
#include <cstdio>
#include <cmath>
using namespace std;
int main()
{
int r, x, y;
scanf("%d%d%d", &r, &x, &y);
long double dist = hypotl(x, y);
if(dist == r) puts("1");
else if(dist < r) puts("2");
else printf("%d\n", int(ceill(dist / r)));
return 0;
}
D - Send More Money
注意:本题的时间限制是 5 5 5秒。
题目大意
给定三个由小写字母组成的字符串
S
1
,
S
2
,
S
3
S_1,S_2,S_3
S1,S2,S3。
在这里,相同的字母表示相同的数字,不同的字母表示不同的数字。
我们要找到一种每个字母对应的数字,并将它们分别填入
S
1
,
S
2
,
S
3
S_1,S_2,S_3
S1,S2,S3,变成三个无前导
0
0
0的正整数
N
1
,
N
2
,
N
3
N_1,N_2,N_3
N1,N2,N3,使得
N
1
+
N
2
=
N
3
N_1+N_2=N_3
N1+N2=N3。如果有多组解,找到任意一组即可。
1 ≤ ∣ S 1 ∣ , ∣ S 2 ∣ , ∣ S 3 ∣ ≤ 10 1\le |S_1|,|S_2|,|S_3|\le 10 1≤∣S1∣,∣S2∣,∣S3∣≤10
输入格式
S
1
S_1
S1
S
2
S_2
S2
S
3
S_3
S3
输出格式
如果有解,输出三行,即
N
1
,
N
2
N_1,N_2
N1,N2和
N
3
N_3
N3;如果无解,输出UNSOLVABLE
。
样例
|
S
1
S_1
S1|
S
2
S_2
S2|
S
3
S_3
S3|输出|
|–|–|–|–|–|–|
|a
|b
|c
|1 2 3
|
|x
|x
|y
|1 1 2
|
|p
|q
|p
|UNSOLVABLE
|
分析
很容易想到,因为每位能填的数字只有
0
0
0到
9
9
9(一共
10
10
10个),所以如果
S
1
,
S
2
S_1,S_2
S1,S2和
S
3
S_3
S3中总共不同的字母个数超过
10
10
10个,我们就可以直接认为这个等式无解。
现在,由于这道题时间限制较长,我们就可以暴力枚举每一种字母对应的数字(一共最多有
10
!
=
3628800
10!=3628800
10!=3628800种可能),再逐个填入并验证即可。
代码
这里的枚举我是用全排列做的,当然也可以用回溯法等,方法不限。
#include <cstdio>
#include <set>
#include <algorithm>
#define maxl 15
using namespace std;
using LL = long long;
char s1[maxl], s2[maxl], s3[maxl], ch[maxl];
set<char> chars;
int pos[26], num[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
#define N (num[pos[str[i] - 'a']])
inline LL parse(const char* str)
{
LL res = 0LL;
for(int i=0; str[i]; i++)
res = res * 10LL + N;
return res;
}
inline void print(const char* str)
{
for(int i=0; str[i]; i++)
putchar(N + '0');
putchar('\n');
}
#undef N
inline void add(const char* str)
{
for(int i=0; str[i]; i++)
chars.insert(str[i]);
}
inline bool isok(const char* str)
{
return num[pos[str[0] - 'a']] != 0;
}
int main()
{
scanf("%s%s%s", s1, s2, s3);
add(s1), add(s2), add(s3);
if(chars.size() > 11)
{
puts("UNSOLVABLE");
return 0;
}
int cnt = 0;
for(char x: chars)
pos[x - 'a'] = cnt++;
do
{
if(isok(s1) && isok(s2) && isok(s3) && (parse(s1) + parse(s2) == parse(s3)))
{
print(s1);
print(s2);
print(s3);
return 0;
}
} while(next_permutation(num, num + 10));
puts("UNSOLVABLE");
return 0;
}
P.S. 这段代码的运行速度不可思议的快…… 居然只花了 109 109 109ms……
E - Unique Color
题目大意
给你一棵由编号为
1
,
2
,
…
,
N
1,2,~\dots,N
1,2, …,N的
N
N
N个顶点组成的树。其中,第
i
i
i条边连接着顶点
A
i
A_i
Ai和
B
i
B_i
Bi。第
i
i
i个顶点的颜色是
C
i
C_i
Ci(在这里,颜色用一个整数表示)。
我们定义一个顶点
x
x
x是好的,仅当如下条件成立:
- 从顶点 1 1 1到顶点 x x x的最短路径上,没有与顶点 x x x颜色相同的点(顶点 x x x本身除外)。
找到所有的好的顶点。
2
≤
N
≤
1
0
5
2\le N\le 10^5
2≤N≤105
1
≤
C
i
≤
1
0
5
1\le C_i\le 10^5
1≤Ci≤105
1
≤
A
i
,
B
i
≤
1
0
5
1\le A_i,B_i\le 10^5
1≤Ai,Bi≤105
输入格式
N
N
N
C
1
…
C
N
C_1~\dots~C_N
C1 … CN
A
1
B
1
A_1~B_1
A1 B1
⋮
\vdots
⋮
A
N
−
1
B
N
−
1
A_{N-1}~B_{N-1}
AN−1 BN−1
输出格式
输出所有的好的顶点,按升序排序,每行一个。
样例
略,请自行前往AtCoder查看
分析
其实这题用最朴素的
DFS
\text{DFS}
DFS算法就能解决。
我们在搜索的同时,维护一个
used
\text{used}
used数组,记录每个颜色在路径上是否已经用过。这样,我们就能
O
(
1
)
\mathcal O(1)
O(1)地判断每个点是否是一个好的点,最后排序并输出即可。
代码
#include <cstdio>
#include <vector>
#include <set>
#define maxn 100005
using namespace std;
vector<int> G[maxn];
bool used[maxn];
int color[maxn];
set<int> res;
void dfs(int v, int par)
{
if(used[color[v]])
{
for(int u: G[v])
if(u != par)
dfs(u, v);
return;
}
used[color[v]] = true;
res.insert(v);
for(int u: G[v])
if(u != par)
dfs(u, v);
used[color[v]] = false;
}
int main()
{
int n;
scanf("%d", &n);
for(int i=1; i<=n; i++)
scanf("%d", color + i);
while(--n)
{
int x, y;
scanf("%d%d", &x, &y);
G[x].push_back(y);
G[y].push_back(x);
}
dfs(1, -1);
for(int v: res)
printf("%d\n", v);
return 0;
}