文章目录
2020牛客暑期多校训练营(第七场)题解及补题
比赛过程
这场打出了牛客排名新低,原因一来这场题目难度差别较大,我们前期思考的有点慢也wa了很多发罚时炸了,二来读题能力确实不太行,J题读了一小时多才读懂,已经没是没时间写了。
题解
A
题意
给一个半径为 r r r的圆,在圆内找到 n n n个整数点,求 ∑ i = 1 n − 1 ∑ j = i + 1 n d i s t ( i , j ) 2 \sum_{i=1}^{n-1}\sum_{j=i+1}^{n}dist(i,j)^2 ∑i=1n−1∑j=i+1ndist(i,j)2的最大值,其中 d i s t ( i , j ) dist(i,j) dist(i,j)指点 i i i与点 j j j之间的欧几里得距离。
解法
通过拆分计算 ∑ i = 1 n − 1 ∑ j = i + 1 n d i s t ( i , j ) 2 = n ∑ i = 1 n ( x i 2 + y i 2 ) − ( ∑ i = 1 n x i ) 2 − ( ∑ i = 1 n y i ) 2 \sum_{i=1}^{n-1}\sum_{j=i+1}^{n}dist(i,j)^2=n\sum_{i=1}^{n}(x_{i}^2+y_{i}^2)-(\sum_{i=1}^{n}x_{i})^2-(\sum_{i=1}^{n}y_{i})^2 ∑i=1n−1∑j=i+1ndist(i,j)2=n∑i=1n(xi2+yi2)−(∑i=1nxi)2−(∑i=1nyi)2,维护一个 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]表示前 i i i个点横坐标和为 j j j,纵坐标和为 k k k每个点到圆心距离的平方和的最大值,根据 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]更新 a n s [ i ] [ r ] ans[i][r] ans[i][r], a n s [ i ] [ r ] = m a x ( i ∗ d p [ i ] [ j ] [ k ] − j 2 − k 2 ) ans[i][r]=max(i\ast dp[i][j][k]-j^2-k^2) ans[i][r]=max(i∗dp[i][j][k]−j2−k2)。
代码
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include <bits/stdc++.h>
//#include <ext/pb_ds/assoc_container.hpp>
//#include <ext/pb_ds/tree_policy.hpp>
//#include <ext/pb_ds/priority_queue.hpp>
//using namespace __gnu_pbds;
using namespace std;
#define ll long long
#define ld long double
#define ull unsigned long long
#define mst(a, b) memset((a), (b), sizeof(a))
#define mp(a, b) make_pair(a, b)
#define pi acos(-1)
#define endl '\n'
#define pii pair<int, int>
#define pll pair<ll, ll>
#define pdd pair<double, double>
#define vi vector<int>
#define vl vector<ll>
#define pb push_back
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
#define lowbit(x) x &(-x)
#define all(x) (x).begin(), (x).end()
#define sf(x) scanf("%d", &x)
#define pf(x) printf("%d\n", x)
#define debug(x) cout << x << endl
#define mod(x) (x % mod + mod) % mod
template <typename T>
void read(T &x) {
x = 0;
char ch = getchar();
ll f = 1;
while (!isdigit(ch)) {
if (ch == '-')
f *= -1;
ch = getchar();
}
while (isdigit(ch)) {
x = x * 10 + ch - 48;
ch = getchar();
}
x *= f;
}
const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-8;
const int maxn = 3e3 + 7;
const int maxm = 1e5 + 7;
const int mod = 1e9 + 7;
#define IO ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
//template<typename T> using ordered_set = tree<T,null_type,less<T>,rb_tree_tag,tree_order_statistics_node_update>;
struct node {
int r, x, y;
} q[maxn];
bool cmp(node a, node b) {
return a.r < b.r;
}
int dp[10][606][606], ans[10][35], tot = 0;
void init() {
for (int i = -30; i <= 30; ++i)
for (int j = -30; j <= 30; ++j)
if (i * i + j * j <= 900)
q[++tot] = {i * i + j * j, i, j};
sort(q + 1, q + 1 + tot, cmp);
mst(dp, -INF);
int cnt = 1;
dp[0][300][300] = 0;
for (int r = 1; r <= 30; ++r) {
for (; cnt <= tot && q[cnt].r <= r * r; ++cnt) {
node res = q[cnt];
for (int i = 1; i <= 8; ++i)
for (int j = -r * i + 300; j <= r * i + 300; ++j)
for (int k = -r * i + 300; k <= r * i + 300; ++k)
dp[i][j][k] = max(dp[i][j][k], dp[i - 1][j - res.x][k - res.y] + res.r);
}
for (int i = 1; i <= 8; ++i)
for (int j = -r * i; j <= r * i; ++j)
for (int k = -r * i; k <= r * i; ++k)
ans[i][r] = max(ans[i][r], i * max(dp[i][j + 300][k + 300], 0) - j * j - k * k);
}
}
int main() {
IO;
init();
int t;
cin >> t;
while (t--) {
int n, r;
cin >> n >> r;
cout << ans[n][r] << endl;
}
return 0;
}
B
题意
n ⋅ m n \cdot m n⋅m个口罩,装最少的箱,使得在个数平均的情况下,既能分箱分给 n n n 个医院,也能分给 m m m 个医院。
解法
通过画图可知,有一个 n ⋅ m n \cdot m n⋅m 的矩形,这个矩形可以是立着的也可以是躺着的,那么可以不断的填充最大的正方形,因为正方形是旋转 90 90 90 度不变的,所以可以将这个矩形不断填充最大的正方形,进而转换为子问题.过程类似gcd.
代码
#include <stdio.h>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 111;
const ll mod = 1e9 + 7;
#define P pair<int, int>
vector<int> ans;
void solve(int x, int y) {
if (x == 0 || y == 0) return;
if (x < y) swap(x, y);
x -= y;
for (int i = 1; i <= y; ++i) ans.push_back(y);
solve(x, y);
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
ans.clear();
int n, m;
scanf("%d%d", &n, &m);
solve(n, m);
int rr = ans.size();
printf("%d\n", rr);
for (int i = 0; i < rr; ++i) {
printf("%d%c", ans[i], i < rr - 1 ? ' ' : '\n');
}
}
return 0;
}
C
题意
有一棵树,三种操作:
∙
\bullet
∙ 选定一个中心城市
x
x
x ,其他所有城市的值加
w
−
d
i
s
t
(
x
,
y
)
w-dist(x,y)
w−dist(x,y)
∙
\bullet
∙ 将城市
x
x
x 的值与
0
0
0 取min
∙
\bullet
∙ 询问单点的值
解法
首先,操作2可以记录一个
d
e
l
t
a
delta
delta 数组,判断当前的值并进行改变。
对于操作1,可以发现
w
−
d
i
s
t
(
x
,
y
)
=
w
−
d
e
p
[
x
]
−
d
e
p
[
y
]
+
2
⋅
d
e
p
[
l
c
a
(
x
,
y
)
]
w-dist(x,y)=w-dep[x]-dep[y]+2\cdot dep[lca(x,y)]
w−dist(x,y)=w−dep[x]−dep[y]+2⋅dep[lca(x,y)]
那么我们可以维护一个全局变量
s
u
m
sum
sum ,操作次数为
c
n
t
t
cntt
cntt,每次进行1操作, sum+=w-dep[x]。这样前三项都维护好了,那么怎么计算
d
e
p
[
l
c
a
(
x
,
y
)
]
dep[lca(x,y)]
dep[lca(x,y)] 呢。
显然
l
c
a
(
x
,
y
)
lca(x,y)
lca(x,y) 一定在
x
,
y
x,y
x,y 的上方,那么对于每一次操作1,都把从
x
x
x 到根节点的值+1,那么
d
e
p
[
l
c
a
(
x
,
y
)
]
dep[lca(x,y)]
dep[lca(x,y)] 的值就等于从
y
y
y 到根节点所有的值相加,使用轻重链剖分来维护即可。
那么对某一个点
y
y
y 的询问,
v
a
l
[
y
]
=
s
u
m
−
c
n
t
t
⋅
d
e
p
[
y
]
+
2
∗
q
u
e
r
y
(
1
,
y
)
val[y]=sum-cntt \cdot dep[y]+2*query(1,y)
val[y]=sum−cntt⋅dep[y]+2∗query(1,y)
代码
#pragma region
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
#define tr t[root]
#define lson t[root << 1]
#define rson t[root << 1 | 1]
#define rep(i, a, n) for (ll i = a; i <= n; ++i)
#define per(i, a, n) for (ll i = n; i >= a; --i)
namespace fastIO {
#define BUF_SIZE 100000
#define OUT_SIZE 100000
//fread->R
bool IOerror = 0;
//inline char nc(){char ch=getchar();if(ch==-1)IOerror=1;return ch;}
inline char nc() {
static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE;
if (p1 == pend) {
p1 = buf;
pend = buf + fread(buf, 1, BUF_SIZE, stdin);
if (pend == p1) {
IOerror = 1;
return -1;
}
}
return *p1++;
}
inline bool blank(char ch) { return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; }
template <class T>
inline bool R(T &x) {
bool sign = 0;
char ch = nc();
x = 0;
for (; blank(ch); ch = nc())
;
if (IOerror)
return false;
if (ch == '-')
sign = 1, ch = nc();
for (; ch >= '0' && ch <= '9'; ch = nc())
x = x * 10 + ch - '0';
if (sign)
x = -x;
return true;
}
inline bool R(double &x) {
bool sign = 0;
char ch = nc();
x = 0;
for (; blank(ch); ch = nc())
;
if (IOerror)
return false;
if (ch == '-')
sign = 1, ch = nc();
for (; ch >= '0' && ch <= '9'; ch = nc())
x = x * 10 + ch - '0';
if (ch == '.') {
double tmp = 1;
ch = nc();
for (; ch >= '0' && ch <= '9'; ch = nc())
tmp /= 10.0, x += tmp * (ch - '0');
}
if (sign)
x = -x;
return true;
}
inline bool R(char *s) {
char ch = nc();
for (; blank(ch); ch = nc())
;
if (IOerror)
return false;
for (; !blank(ch) && !IOerror; ch = nc())
*s++ = ch;
*s = 0;
return true;
}
inline bool R(char &c) {
c = nc();
if (IOerror) {
c = -1;
return false;
}
return true;
}
template <class T, class... U>
bool R(T &h, U &... t) { return R(h) && R(t...); }
#undef OUT_SIZE
#undef BUF_SIZE
}; // namespace fastIO
using namespace fastIO;
template <class T>
void _W(const T &x) { cout << x; }
void _W(const int &x) { printf("%d", x); }
void _W(const int64_t &x) { printf("%lld", x); }
void _W(const double &x) { printf("%.16f", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
template <class T, class U>
void _W(const pair<T, U> &x) { _W(x.F), putchar(' '), _W(x.S); }
template <class T>
void _W(const vector<T> &x) {
for (auto i = x.begin(); i != x.end(); _W(*i++))
if (i != x.cbegin()) putchar(' ');
}
void W() {}
template <class T, class... U>
void W(const T &head, const U &... tail) { _W(head), putchar(sizeof...(tail) ? ' ' : '\n'), W(tail...); }
#pragma endregion
const int maxn = 5e4 + 5;
int n, m;
vector<int> g[maxn];
int fa[maxn], sz[maxn], dep[maxn], son[maxn];
int id[maxn], cnt, top[maxn];
int delta[maxn], sum, cntt;
void init() {
rep(i, 1, n) {
g[i].clear();
cntt = sum = delta[i] = 0;
son[i] = fa[i] = sz[i] = dep[i] = 0;
cnt = 0;
top[i] = 0;
}
}
void dfs1(int u, int f, int deep) {
dep[u] = deep, fa[u] = f, sz[u] = 1;
for (auto v : g[u]) {
if (v == f) continue;
dfs1(v, u, deep + 1);
sz[u] += sz[v];
if (sz[v] > sz[son[u]]) son[u] = v;
}
}
void dfs2(int u, int topf) {
id[u] = ++cnt;
top[u] = topf;
if (!son[u]) return;
dfs2(son[u], topf);
for (auto v : g[u]) {
if (v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
}
struct segtree {
int l, r, val, lazy;
} t[maxn << 2];
void build(int root, int l, int r) {
tr.l = l, tr.r = r, tr.lazy = 0, tr.val = 0;
if (l == r) return;
int mid = (l + r) >> 1;
build(root << 1, l, mid);
build(root << 1 | 1, mid + 1, r);
tr.val = lson.val + rson.val;
}
void spread(int root) {
if (tr.lazy) {
lson.val += tr.lazy * (lson.r - lson.l + 1);
rson.val += tr.lazy * (rson.r - rson.l + 1);
lson.lazy += tr.lazy;
rson.lazy += tr.lazy;
tr.lazy = 0;
}
}
int query(int root, int l, int r) {
if (l <= tr.l && tr.r <= r) return tr.val;
spread(root);
int ans = 0;
int mid = (tr.l + tr.r) >> 1;
if (l <= mid) ans += query(root << 1, l, r);
if (r > mid) ans += query(root << 1 | 1, l, r);
return ans;
}
void update(int root, int l, int r, int x) {
if (l <= tr.l && tr.r <= r) {
tr.val += x * (tr.r - tr.l + 1);
tr.lazy += x;
return;
}
spread(root);
int mid = (tr.l + tr.r) >> 1;
if (l <= mid) update(root << 1, l, r, x);
if (r > mid) update(root << 1 | 1, l, r, x);
tr.val = lson.val + rson.val;
}
void updRange(int x, int y, int k) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
update(1, id[top[x]], id[x], k);
x = fa[top[x]];
}
if (dep[x] > dep[y]) swap(x, y);
update(1, id[x], id[y], k);
}
int qRange(int x, int y) {
int ans = 0;
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
ans += query(1, id[top[x]], id[x]);
x = fa[top[x]];
}
if (dep[x] > dep[y]) swap(x, y);
ans += query(1, id[x], id[y]);
return ans;
}
int solve(int x) {
return sum - cntt * dep[x] + 2 * qRange(1, x);
}
int main() {
int T;
R(T);
while (T--) {
R(n, m);
init();
rep(i, 1, n - 1) {
int u, v;
R(u, v);
g[u].push_back(v);
g[v].push_back(u);
}
dfs1(1, 0, 1);
dfs2(1, 1);
build(1, 1, n);
while (m--) {
int op, x, w;
R(op);
if (op == 1) {
R(x, w);
sum += w - dep[x];
cntt++;
updRange(1, x, 1);
} else if (op == 2) {
R(x);
ll tmp = solve(x) + delta[x];
if (tmp > 0) delta[x] -= tmp;
} else {
R(x);
W(solve(x) + delta[x]);
}
}
}
}
D
题意
给一个 n n n ($1\le n \le 10^{15} $),判断 ∑ k = 1 n k 2 \sum_{k=1}^{n} k^2 ∑k=1nk2是不是一个平方数.
解法
打表可知只有 n = 1 n=1 n=1 或 n = 24 n=24 n=24 时结果是一个平方数.
代码
#include <stdio.h>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 111;
const int mod = 1e9 + 7;
#define P pair<int, int>
int main() {
int T;
scanf("%d", &T);
while (T--) {
ll n;
scanf("%lld", &n);
if (n == 1 || n == 24) {
puts("Fake news!");
} else {
puts("Nobody knows it better than me!");
}
}
return 0;
}
E
题意
解法
代码
F
题意
解法
代码
G
题意
解法
代码
H
题意
∙
\bullet
∙ (1,k) 是一个Legend Tuple
∙
\bullet
∙ 如果 (n,k) 是一个Legend Tuple,那么(n+k,k) 和 (n
⋅
\cdot
⋅k,k)也是一个Legend Tuple.
求在范围
1
≤
n
≤
N
1 \le n \le N
1≤n≤N ,
1
≤
k
≤
K
1 \le k \le K
1≤k≤K 的范围内有多少组(n,k)是Legend Tuple.
解法
∙
\bullet
∙ 首先 (1,k) 是一个Legend Tuple
∙
\bullet
∙ 那么当
n
%
k
=
=
1
n\%k==1
n%k==1 时,由加法运算可知(n,k)是一个Legend Tuple.
∙
\bullet
∙ 由乘法运算可知, (k,k)是一个Legend Tuple,再由加法运算可知当 $n%k==0 $ 时, (n,k)是一个Legend Tuple.
如果 (n,k) 是一个Legend Tuple
如果只使用加法运算,那么满足
n
%
k
=
=
1
n\%k==1
n%k==1,如果使用了乘法运算,那么一定有
n
%
k
=
=
0
n\%k==0
n%k==0 ,前面已经证明当
n
n
n 是
k
k
k 的倍数时,(n,k)是一个Legend Tuple.
所以,(n,k)是一个Legend Tuple的充要条件为
n
%
k
=
=
0
∣
∣
n
%
k
=
=
1
n\%k==0||n\%k==1
n%k==0∣∣n%k==1.
对于每一个
k
(
k
≥
2
)
k(k\ge 2)
k(k≥2) ,
n
n
n 可取
1
,
k
,
k
+
1
,
2
⋅
k
,
…
1,k,k+1,2 \cdot k,\dots
1,k,k+1,2⋅k,… ,也就是 $ 2\cdot \left \lfloor \frac{N}{k} \right \rfloor + (N%k!=0)$.
显然当
k
=
1
k=1
k=1 时有
n
n
n 组.
综上,答案为
∑
k
=
1
K
{
2
⋅
⌊
N
k
⌋
+
(
N
%
k
!
=
0
)
}
\sum_{k=1}^{K} \left \{ 2\cdot \left \lfloor \frac{N}{k} \right \rfloor + (N\%k!=0)\right \}
∑k=1K{2⋅⌊kN⌋+(N%k!=0)},前面部分使用除法分块,后面部分筛一下
N
N
N 的因子即可.
注意乘法会暴longlong ,需要使用快速乘.
代码
#pragma region
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
#define rep(i, a, n) for (ll i = a; i <= n; ++i)
#define per(i, a, n) for (ll i = n; i >= a; --i)
namespace fastIO {
#define BUF_SIZE 100000
#define OUT_SIZE 100000
//fread->R
bool IOerror = 0;
//inline char nc(){char ch=getchar();if(ch==-1)IOerror=1;return ch;}
inline char nc() {
static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE;
if (p1 == pend) {
p1 = buf;
pend = buf + fread(buf, 1, BUF_SIZE, stdin);
if (pend == p1) {
IOerror = 1;
return -1;
}
}
return *p1++;
}
inline bool blank(char ch) { return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; }
template <class T>
inline bool R(T &x) {
bool sign = 0;
char ch = nc();
x = 0;
for (; blank(ch); ch = nc())
;
if (IOerror)
return false;
if (ch == '-')
sign = 1, ch = nc();
for (; ch >= '0' && ch <= '9'; ch = nc())
x = x * 10 + ch - '0';
if (sign)
x = -x;
return true;
}
inline bool R(double &x) {
bool sign = 0;
char ch = nc();
x = 0;
for (; blank(ch); ch = nc())
;
if (IOerror)
return false;
if (ch == '-')
sign = 1, ch = nc();
for (; ch >= '0' && ch <= '9'; ch = nc())
x = x * 10 + ch - '0';
if (ch == '.') {
double tmp = 1;
ch = nc();
for (; ch >= '0' && ch <= '9'; ch = nc())
tmp /= 10.0, x += tmp * (ch - '0');
}
if (sign)
x = -x;
return true;
}
inline bool R(char *s) {
char ch = nc();
for (; blank(ch); ch = nc())
;
if (IOerror)
return false;
for (; !blank(ch) && !IOerror; ch = nc())
*s++ = ch;
*s = 0;
return true;
}
inline bool R(char &c) {
c = nc();
if (IOerror) {
c = -1;
return false;
}
return true;
}
template <class T, class... U>
bool R(T &h, U &... t) { return R(h) && R(t...); }
#undef OUT_SIZE
#undef BUF_SIZE
}; // namespace fastIO
using namespace fastIO;
template <class T>
void _W(const T &x) { cout << x; }
void _W(const int &x) { printf("%d", x); }
void _W(const int64_t &x) { printf("%lld", x); }
void _W(const double &x) { printf("%.16f", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
template <class T, class U>
void _W(const pair<T, U> &x) { _W(x.F), putchar(' '), _W(x.S); }
template <class T>
void _W(const vector<T> &x) {
for (auto i = x.begin(); i != x.end(); _W(*i++))
if (i != x.cbegin()) putchar(' ');
}
void W() {}
template <class T, class... U>
void W(const T &head, const U &... tail) { _W(head), putchar(sizeof...(tail) ? ' ' : '\n'), W(tail...); }
#pragma endregion
const ll mod = 1e9 + 7;
ll mul(ll a, ll b) {
ll ans = 0;
while (b) {
if (b & 1) ans = (ans + a) % mod;
a = (a + a) % mod;
b >>= 1;
}
return ans;
}
int main() {
ll n, k;
R(n, k);
ll ans = 0;
for (ll l = 1, r; l <= k; l = r + 1) {
r = (n / l ? min(k, n / (n / l)) : k);
ans = (ans + mul(n / l, r - l + 1)) % mod;
}
ans = (ans + ans - n + mod) % mod; //对1的修正
ans = (ans + k) % mod;
for (ll i = 1; i * i <= n; ++i) {
if (n % i == 0) {
if (i <= k) --ans;
if (i * i != n && n / i <= k) --ans;
}
}
W((ans + mod) % mod);
}
I
题意
解法
代码
J
题意
一个程序内有四种语句,且程序对语句顺序不敏感(可能以任意顺序执行),问每个指针最后可能指向的对象集合。
解法
读题题,读完题类似建图的直接模拟就行。
代码
#include <stdio.h>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 111;
const ll mod = 1e9 + 7;
#define P pair<int, int>
char s[300][200];
int tmp[30][30][30], G[30][30];
int n;
void solve() {
for (int i = 1; i <= n; ++i) {
if (s[i][1] == '.') { // 3
char a = s[i][0], b = s[i][6];
for (int j = 0; j < 26; ++j) {
if (G[a - 'A'][j]) {
for (int k = 0; k < 26; ++k) {
if (G[b - 'A'][k]) tmp[j][s[i][2] - 'a'][k] = 1;
}
}
}
} else if (s[i][5] == '.') { // 4
char a = s[i][0], b = s[i][4];
for (int j = 0; j < 26; ++j) {
if (G[b - 'A'][j]) {
for (int k = 0; k < 26; ++k) {
if (tmp[j][s[i][6] - 'a'][k]) {
G[s[i][0] - 'A'][k] = 1;
}
}
}
}
} else if (s[i][4] >= 'a' && s[i][4] <= 'z') { // 1
char a = s[i][0], b = s[i][4];
G[a - 'A'][b - 'a'] = 1;
} else { // 2
char a = s[i][0], b = s[i][4];
for (int j = 0; j < 26; ++j) {
if (G[b - 'A'][j]) {
G[a - 'A'][j] = 1;
}
}
}
}
}
int main() {
scanf("%d", &n);
getchar();
for (int i = 1; i <= n; ++i) {
gets(s[i]);
}
solve();
int last = -1;
for (int i = 1; i <= 55; ++i) solve();
for (int i = 0; i < 26; ++i) {
printf("%c: ", 'A' + i);
for (int j = 0; j < 26; ++j) {
if (G[i][j]) printf("%c", j + 'a');
}
puts("");
}
return 0;
}