AISing Programming Contest 2021 (AtCoder Beginner Contest 202) A~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 1a,b,c6

输入格式

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 (7a)+(7b)+(7c)=21abc

代码

#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°

题目大意

给定一个由01689组成的字符串 S S S。将其旋转 180 ° 180\degree 180°并输出。

一个字符串旋转 180 ° 180\degree 180°的方法:

  • 将其翻转(reverse)。
  • 将其中的6替换为99替换为6

1 ≤ ∣ S ∣ ≤ 1 0 5 1\le |S|\le10^5 1S105

输入格式

S S S

输出格式

输出 S S S旋转 180 ° 180\degree 180°后的字符串。

样例

S S S输出
06018896881090
8691001698
0101001010

分析

本题直接按要求模拟即可。

代码

#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 1N105
1 ≤ A i , B i , C i ≤ N 1\le A_i,B_i,C_i\le N 1Ai,Bi,CiN

输入格式

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=1nacntibcnti

代码

注意:不要忘记使用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 Aa B B Bb(均不要求连续)组成的字符串中,求字典序第 K K K小的。

1 ≤ A , B ≤ 30 1\le A,B\le 30 1A,B30
1 ≤ K ≤ S 1\le K\le S 1KS S S S为由 A A Aa B B Bb组成的字符串的个数)

输入格式

A   B   K A~B~K A B K

输出格式

输出由 A A Aa B B Bb组成的字符串中字典序第 K K K小的。

样例

A A A B B B K K K输出
2 2 2 2 2 2 4 4 4baab
30 30 30 30 30 30 118264581564861424 118264581564861424 118264581564861424 30 30 30b + 30 +30 +30a)

分析

我们令 d p ( a , b ) \mathrm{dp}(a,b) dp(a,b)为由 a a aa b b bb组成的字符串的个数,则:

  • 我们在长度为 a + b − 1 a+b-1 a+b1的字符串上再添上一个ab
  • 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(a1,b)+dp(a,b1)

我们令 f ( a , b , k ) f(a,b,k) f(a,b,k)为由 A A Aa B B Bb组成的字符串中字典序第 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(a1,b,k)‘‘b"+f(a,b1,k)‘‘a"+f(a1,b,k)‘‘b"+f(a,b1,kdp(a1,b))(a=b=0)(b=0)(a=0)(kdp(a1,b))(k>dp(a1,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 2iN)的父亲节点是 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 1N2×105
1 ≤ P i < i 1\le P_i < i 1Pi<i
1 ≤ Q ≤ 2 × 1 0 5 1\le Q\le 2\times10^5 1Q2×105
1 ≤ U i ≤ N 1\le U_i\le N 1UiN
0 ≤ D i < N 0\le D_i < N 0Di<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 invinu<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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值