ABC202/AISing2021 A-E
- [A - Three Dice](https://atcoder.jp/contests/abc202/tasks/abc202_a)
- [B - 180°](https://atcoder.jp/contests/abc202/tasks/abc202_b)
- [C - Made Up](https://atcoder.jp/contests/abc202/tasks/abc202_c)
- [D - aab aba baa](https://atcoder.jp/contests/abc202/tasks/abc202_d)
- [E - Count Descendants](https://atcoder.jp/contests/abc202/tasks/abc202_e)
A - Three Dice
题目大意
一个人抛了三个骰子,它们的顶面分别是
a
,
b
,
c
a,b,c
a,b,c。求它们的底面之和。
这里用的骰子是标准骰子,即两个相对的面之和为
7
7
7。
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 a a | b b b | c c c | 答案 |
---|---|---|---|
1 1 1 | 4 4 4 | 3 3 3 | 13 13 13 |
5 5 5 | 6 6 6 | 4 4 4 | 6 6 6 |
分析
因为两个相对的面之和为 7 7 7,所以本题的答案为 ( 7 − a ) + ( 7 − b ) + ( 7 − c ) = 21 − a − b − c (7-a)+(7-b)+(7-c)=21-a-b-c (7−a)+(7−b)+(7−c)=21−a−b−c。
代码
#include <cstdio>
using namespace std;
int main()
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
printf("%d\n", 21 - a - b - c);
return 0;
}
B - 180°
题目大意
给定一个由0
、1
、6
、8
、9
组成的字符串
S
S
S。将其旋转
180
°
180\degree
180°并输出。
一个字符串旋转 180 ° 180\degree 180°的方法:
- 将其翻转(
reverse
)。- 将其中的
6
替换为9
,9
替换为6
。
1 ≤ ∣ S ∣ ≤ 1 0 5 1\le |S|\le10^5 1≤∣S∣≤105
输入格式
S S S
输出格式
输出 S S S旋转 180 ° 180\degree 180°后的字符串。
样例
S S S | 输出 |
---|---|
0601889 | 6881090 |
86910 | 01698 |
01010 | 01010 |
分析
本题直接按要求模拟即可。
代码
#include <cstdio>
#define maxn 100005
using namespace std;
char s[maxn];
int main()
{
int n = 0;
char c;
while((c = getchar()) != '\n')
s[n++] = c;
while(n--)
putchar(s[n] == '6'? '9': s[n] == '9'? '6': s[n]);
putchar('\n');
return 0;
}
C - Made Up
题目大意
给定三个长度为
N
N
N的序列:
A
,
B
,
C
A,B,C
A,B,C。
有多少对
(
i
,
j
)
(i,j)
(i,j)符合
A
i
=
B
C
j
A_i=B_{C_j}
Ai=BCj?
1
≤
N
≤
1
0
5
1\le N\le 10^5
1≤N≤105
1
≤
A
i
,
B
i
,
C
i
≤
N
1\le A_i,B_i,C_i\le N
1≤Ai,Bi,Ci≤N
输入格式
N
N
N
A
1
A
2
…
A
N
A_1~A_2~\dots~A_N
A1 A2 … AN
B
1
B
2
…
B
N
B_1~B_2~\dots~B_N
B1 B2 … BN
C
1
C
2
…
C
N
C_1~C_2~\dots~C_N
C1 C2 … CN
输出格式
输出符合 A i = B C j A_i=B_{C_j} Ai=BCj的 ( i , j ) (i,j) (i,j)的对数。
样例
样例输入1
3
1 2 2
3 1 2
2 3 2
样例输出1
4
4 4 4对 ( i , j ) (i,j) (i,j)符合条件: ( 1 , 1 ) , ( 1 , 3 ) , ( 2 , 2 ) , ( 3 , 2 ) (1,1),(1,3),(2,2),(3,2) (1,1),(1,3),(2,2),(3,2)。
样例输入2
4
1 1 1 1
1 1 1 1
1 2 3 4
样例输出2
16
所有 ( i , j ) (i,j) (i,j)都符合条件。
样例输入3
3
2 3 3
1 3 3
1 1 1
样例输出3
0
没有 ( i , j ) (i,j) (i,j)符合条件。
分析
我们很容易想到
O
(
n
2
)
O(n^2)
O(n2)的算法:暴力枚举所有
(
i
,
j
)
(i,j)
(i,j),并统计符合条件的对数。
可惜,这样会TLE
。
我们考虑将所有的
A
i
A_i
Ai和
B
C
j
B_{C_j}
BCj分别放入两个桶
a
c
n
t
\mathrm{acnt}
acnt和
b
c
n
t
\mathrm{bcnt}
bcnt。
根据乘法原理我们得出答案为
∑
i
=
1
n
a
c
n
t
i
b
c
n
t
i
\sum\limits_{i=1}^n\mathrm{acnt}_i\mathrm{bcnt}_i
i=1∑nacntibcnti。
代码
注意:不要忘记使用long long
!
#include <cstdio>
#define maxn 100005
using namespace std;
using LL = long long;
int acnt[maxn], b[maxn], bcnt[maxn];
int main()
{
int n;
scanf("%d", &n);
for(int i=0; i<n; i++)
{
int a;
scanf("%d", &a);
acnt[a] ++;
}
for(int i=0; i<n; i++)
scanf("%d", b + i);
for(int i=0; i<n; i++)
{
int c;
scanf("%d", &c);
bcnt[b[--c]] ++;
}
LL ans = 0LL;
for(int i=1; i<=n; i++)
ans += LL(acnt[i]) * LL(bcnt[i]);
printf("%lld\n", ans);
return 0;
}
D - aab aba baa
题目大意
在由
A
A
A个a
和
B
B
B个b
(均不要求连续)组成的字符串中,求字典序第
K
K
K小的。
1
≤
A
,
B
≤
30
1\le A,B\le 30
1≤A,B≤30
1
≤
K
≤
S
1\le K\le S
1≤K≤S(
S
S
S为由
A
A
A个a
和
B
B
B个b
组成的字符串的个数)
输入格式
A B K A~B~K A B K
输出格式
输出由
A
A
A个a
和
B
B
B个b
组成的字符串中字典序第
K
K
K小的。
样例
A A A | B B B | K K K | 输出 |
---|---|---|---|
2 2 2 | 2 2 2 | 4 4 4 | baab |
30 30 30 | 30 30 30 | 118264581564861424 118264581564861424 118264581564861424 | (
30
30
30个b
+
30
+30
+30个a ) |
分析
我们令
d
p
(
a
,
b
)
\mathrm{dp}(a,b)
dp(a,b)为由
a
a
a个a
和
b
b
b个b
组成的字符串的个数,则:
- 我们在长度为
a
+
b
−
1
a+b-1
a+b−1的字符串上再添上一个
a
或b
: - d p ( a , b ) = d p ( a − 1 , b ) + d p ( a , b − 1 ) \mathrm{dp}(a,b)=\mathrm{dp}(a-1,b)+\mathrm{dp}(a,b-1) dp(a,b)=dp(a−1,b)+dp(a,b−1)。
我们令
f
(
a
,
b
,
k
)
f(a,b,k)
f(a,b,k)为由
A
A
A个a
和
B
B
B个b
组成的字符串中字典序第
K
K
K小的字符串,则有如下递推式(这里的加法表示字符串连接):
f
(
a
,
b
,
k
)
=
{
‘
‘
"
(
a
=
b
=
0
)
‘
‘
a
"
+
f
(
a
−
1
,
b
,
k
)
(
b
=
0
)
‘
‘
b
"
+
f
(
a
,
b
−
1
,
k
)
(
a
=
0
)
‘
‘
a
"
+
f
(
a
−
1
,
b
,
k
)
(
k
≤
d
p
(
a
−
1
,
b
)
)
‘
‘
b
"
+
f
(
a
,
b
−
1
,
k
−
d
p
(
a
−
1
,
b
)
)
(
k
>
d
p
(
a
−
1
,
b
)
)
f(a,b,k)=\begin{cases} ``" & (a=b=0)\\ ``a"+f(a-1,b,k) & (b=0)\\ ``b"+f(a,b-1,k) & (a=0)\\ ``a"+f(a-1,b,k) & (k\le \mathrm{dp}(a-1,b))\\ ``b"+f(a,b-1,k- \mathrm{dp}(a-1,b)) & (k>\mathrm{dp}(a-1,b)) \end{cases}
f(a,b,k)=⎩
⎨
⎧‘‘"‘‘a"+f(a−1,b,k)‘‘b"+f(a,b−1,k)‘‘a"+f(a−1,b,k)‘‘b"+f(a,b−1,k−dp(a−1,b))(a=b=0)(b=0)(a=0)(k≤dp(a−1,b))(k>dp(a−1,b))
代码
写代码时,可以用递归形式,也可以使用非递归形式(更快):
#include <cstdio>
#define maxn 35
using namespace std;
using LL = long long;
LL dp[maxn][maxn];
int main()
{
int a, b;
LL k;
scanf("%d%d%lld", &a, &b, &k);
for(int i=0; i<=a; i++) dp[i][0] = 1;
for(int i=0; i<=b; i++) dp[0][i] = 1;
for(int i=1; i<=a; i++)
for(int j=1; j<=b; j++)
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
while(a && b)
{
LL t = dp[a - 1][b];
if(k <= t) putchar('a'), a --;
else putchar('b'), b --, k -= t;
}
while(a--) putchar('a');
while(b--) putchar('b');
putchar('\n');
return 0;
}
E - Count Descendants
题目大意
我们有一棵
N
N
N个节点的树,节点的编号分别为
1
,
2
,
…
,
N
1,2,\dots,N
1,2,…,N。
1
1
1号点是根节点,且第
i
i
i个点(
2
≤
i
≤
N
2\le i\le N
2≤i≤N)的父亲节点是
P
i
P_i
Pi。
给你
Q
Q
Q个查询,第
i
i
i个查询包含两个整数
U
i
U_i
Ui和
D
i
D_i
Di,求符合下列条件的点
u
u
u的个数:
- u u u到根节点的最短路径正好有 D i D_i Di条边;
- U i U_i Ui在 u u u到根节点的最短路径中(包含两端)。
1
≤
N
≤
2
×
1
0
5
1\le N\le 2\times10^5
1≤N≤2×105
1
≤
P
i
<
i
1\le P_i < i
1≤Pi<i
1
≤
Q
≤
2
×
1
0
5
1\le Q\le 2\times10^5
1≤Q≤2×105
1
≤
U
i
≤
N
1\le U_i\le N
1≤Ui≤N
0
≤
D
i
<
N
0\le D_i < N
0≤Di<N
输入格式
N
N
N
P
2
P
3
…
P
N
P_2~P_3~\dots~P_N
P2 P3 … PN
Q
Q
Q
U
1
D
1
U_1~D_1
U1 D1
U
2
D
2
U_2~D_2
U2 D2
⋮
\vdots
⋮
U
Q
D
Q
U_Q~D_Q
UQ DQ
输出格式
输出 Q Q Q行。第 i i i行包含对第 i i i个查询的回应。
样例
样例输入
7
1 1 2 2 4 2
4
1 2
7 2
4 1
5 5
样例输出
3
1
0
0
在第一个查询中,节点
4
,
5
,
7
4,5,7
4,5,7符合条件。
在第二个查询中,只有节点
7
7
7符合条件。
在最后两个查询中,没有节点符合条件。
分析
我们可以先在整棵树上从根节点开始跑一遍
DFS
\text{DFS}
DFS,对于节点
i
i
i预处理出
i
n
i
\mathrm{in}_i
ini和
o
u
t
i
\mathrm{out}_i
outi,分别表示进入和走出这个节点的时间,同时将第
i
i
i层节点的所有
i
n
\mathrm{in}
in放入
d
e
p
i
n
i
\mathrm{depin}_i
depini。
如果节点
u
u
u到根节点的路径中有
v
v
v,则
i
n
v
≤
i
n
u
<
o
u
t
v
\mathrm{in}_v\le\mathrm{in}_u < \mathrm{out}_v
inv≤inu<outv。
因此,对于每个查询,我们利用二分查找即可快速算出符合条件的节点个数。
代码
#include <cstdio>
#include <vector>
#include <algorithm>
#define maxn 200005
using namespace std;
int in[maxn], out[maxn], dep[maxn], cnt;
vector<int> G[maxn], depin[maxn];
void dfs(int v)
{
depin[dep[v]].push_back(in[v] = cnt++);
for(int u: G[v])
dfs(u);
out[v] = cnt++;
}
int main()
{
int n;
scanf("%d", &n);
dep[0] = cnt = 0;
for(int i=1; i<n; i++)
{
int p;
scanf("%d", &p);
dep[i] = dep[--p] + 1;
G[p].push_back(i);
}
dfs(0);
int q;
scanf("%d", &q);
while(q--)
{
int u, d;
scanf("%d%d", &u, &d);
const auto& din = depin[d];
auto first = lower_bound(din.begin(), din.end(), in[--u]);
auto last = lower_bound(din.begin(), din.end(), out[u]);
printf("%lld\n", last - first);
}
return 0;
}