蓝桥杯 2021年省赛真题 (C/C++ 大学B组 )
最近在备考专升本
解析移步对应 Java组 的题解。
#A 空间
本题总分: 5 5 5 分
问题描述
小蓝准备用 256 M B 256\mathrm{MB} 256MB 的内存空间开一个数组,数组的每个元素都是 32 32 32 位二进制整数,如果不考虑程序占用的空间和维护内存需要的辅助空间,请问 256 M B 256\mathrm{MB} 256MB 的空间可以存储多少个 32 32 32 位二进制整数?
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
67108864
calcCode:
#include <stdio.h>
int main() {
printf("%d", 256 >> 2 << 20);
}
checkCode:
#include <stdio.h>
#include <math.h>
int A[67108864];
int main() {
printf("%f", sizeof A / pow(2, 20));
}
虽然 sizeof
是在编译阶段进行的,
从这个意义上看并不是什么非要支持不可的函数。
但 C \mathrm{C} C,行!
#B 卡片
本题总分: 5 5 5 分
问题描述
小蓝有很多数字卡片,每张卡片上都是数字
0
0
0 到
9
9
9。
小蓝准备用这些卡片来拼一些数,他想从
1
1
1 开始拼出正整数,每拼一个,就保存起来,卡片就不能用来拼其它数了。
小蓝想知道自己能从
1
1
1 拼到多少。
例如,当小蓝有
30
30
30 张卡片,其中
0
0
0 到
9
9
9 各
3
3
3 张,则小蓝可以拼出
1
1
1 到
10
10
10,但是拼
11
11
11 时卡片
1
1
1 已经只有一张了,不够拼出
11
11
11。
现在小蓝手里有
0
0
0 到
9
9
9 的卡片各
2021
2021
2021 张,共
20210
20210
20210 张,请问小蓝可以从
1
1
1 拼到多少?
提示:建议使用计算机编程解决问题。
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
3181
calcCode:
#include <stdio.h>
int n, ans, cnt;
int main() {
while (1) {
n = ans;
while (n) {
if (n % 10 == 1) cnt++;
n /= 10;
}
if (cnt < 2021) ans++;
else break;
}
printf("%d", ans);
}
#C 直线
本题总分: 10 10 10 分
问题描述
在平面直角坐标系中,两点可以确定一条直线。如果有多点在一条直线上,那么这些点中任意两点确定的直线是同一条。
给定平面上
2
×
3
2 × 3
2×3 个整点
{
(
x
,
y
)
∣
0
≤
x
<
2
,
0
≤
y
<
3
,
x
∈
Z
,
y
∈
Z
}
\{(x, y)|0 ≤ x < 2, 0 ≤ y < 3, x ∈ Z, y ∈ Z\}
{(x,y)∣0≤x<2,0≤y<3,x∈Z,y∈Z},即横坐标是
0
0
0 到
1
1
1 (包含
0
0
0 和
1
1
1) 之间的整数、纵坐标是
0
0
0 到
2
2
2 (包含
0
0
0 和
2
2
2) 之间的整数的点。这些点一共确定了
11
11
11 条不同的直线。
给定平面上
20
×
21
20 × 21
20×21 个整点
{
(
x
,
y
)
∣
0
≤
x
<
20
,
0
≤
y
<
21
,
x
∈
Z
,
y
∈
Z
}
\{(x, y)|0 ≤ x < 20, 0 ≤ y < 21, x ∈ Z, y ∈ Z\}
{(x,y)∣0≤x<20,0≤y<21,x∈Z,y∈Z},即横坐标是
0
0
0 到
19
19
19 (包含
0
0
0 和
19
19
19) 之间的整数、纵坐标是
0
0
0 到
20
20
20 (包含
0
0
0 和
20
20
20) 之间的整数的点。请问这些点一共确定了多少条不同的直线。
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
40257
calcCode:
#include <stdio.h>
const int X = 20, Y = 21;
int linked[X][Y][X][Y], ans;
int main() {
for (int x1 = 0; x1 < X; x1++)
for (int y1 = 0; y1 < Y; ++y1) {
linked[x1][y1][x1][y1] = 1;
for (int x2 = 0; x2 < X; ++x2)
for (int y2 = 0; y2 < Y; ++y2)
if (!linked[x1][y1][x2][y2]) {
int x = x1, x_offset = x1 - x2;
int y = y1, y_offset = y1 - y2;
while (x >= 0 && x < X && y >= 0 && y < Y) x -= x_offset, y -= y_offset;
for (x += x_offset, y += y_offset; x >= 0 && x < X && y >= 0 && y < Y; x += x_offset, y += y_offset)
for (int xx = x, yy = y; xx >= 0 && xx < X && yy >= 0 && yy < Y; xx += x_offset, yy += y_offset)
linked[x][y][xx][yy] = linked[xx][yy][x][y] = 1;
ans++;
}
}
printf("%d", ans);
}
#D 货物摆放
本题总分: 10 10 10 分
问题描述
小蓝有一个超大的仓库,可以摆放很多货物。
现在,小蓝有
n
n
n 箱货物要摆放在仓库,每箱货物都是规则的正方体。小蓝规定了长、宽、高三个互相垂直的方向,每箱货物的边都必须严格平行于长、宽、高。
小蓝希望所有的货物最终摆成一个大的立方体。即在长、宽、高的方向上分别堆
L
、
W
、
H
L、W、H
L、W、H 的货物,满足
n
=
L
×
W
×
H
n = L × W × H
n=L×W×H。
给定
n
n
n,请问有多少种堆放货物的方案满足要求。
例如,当
n
=
4
n = 4
n=4 时,有以下
6
6
6 种方案:
1
×
1
×
4
1×1×4
1×1×4、
1
×
2
×
2
1×2×2
1×2×2、
1
×
4
×
1
1×4×1
1×4×1、
2
×
1
×
2
2×1×2
2×1×2、
2
×
2
×
1
2 × 2 × 1
2×2×1、
4
×
1
×
1
4 × 1 × 1
4×1×1。
请问,当
n
=
2021041820210418
n = 2021041820210418
n=2021041820210418 (注意有
16
16
16 位数字)时,总共有多少种方案?
提示:建议使用计算机编程解决问题。
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
2430
calcCode:
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
ll N = 2021041820210418, n = 1, ans;
int main() {
priority_queue<int> exps;
for (int i = 2; i <= sqrt(N); i++)
if (N % i == 0) {
int exp = 0;
while (N % i == 0)
N /= i, exp++;
exps.push(exp);
}
if (N != 1) exps.push(1);
for (int i = 2; exps.size(); i++) {
bool flag = 1;
for (int a = 2; a < i; ++a)
if (i % a == 0) flag = 0;
if (flag) n *= pow(i, exps.top()), exps.pop();
}
for (ll i = 1; i <= n; i++)
for (ll j = 1; j <= n; j++)
if (n % (i * j) == 0) ans++;
cout << ans << endl;
}
#E 路径
本题总分: 15 15 15 分
问题描述
小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图中的最短路径。
小蓝的图由
2021
2021
2021 个结点组成,依次编号
1
1
1 至
2021
2021
2021。对于两个不同的结点
a
,
b
a, b
a,b,如果
a
a
a 和
b
b
b 的差的绝对值大于
21
21
21,则两个结点之间没有边相连;如果
a
a
a 和
b
b
b 的差的绝对值小于等于
21
21
21,则两个点之间有一条长度为
a
a
a 和
b
b
b 的最小公倍数的无向边相连。
例如:结点
1
1
1 和结点
23
23
23 之间没有边相连;结点
3
3
3 和结点
24
24
24 之间有一条无向边,长度为
24
24
24;结点
15
15
15 和结点
25
25
25 之间有一条无向边,长度为
75
75
75。
请计算,结点
1
1
1 和结点
2021
2021
2021 之间的最短路径长度是多少。
提示:建议使用计算机编程解决问题。
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
10266837
calcCode:
#include <stdio.h>
#include <string.h>
const int N = 2021;
int map[N + 1][N + 1];
int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }
int lcm(int a, int b) { return a * b / gcd(a, b); }
int min(int a, int b) { return a < b ? a : b; }
int main() {
memset(&map, 0x3F, sizeof map);
for (int u = 1; u <= N; u++)
for (int v = min(N, u + 21); v > u; --v)
map[u][v] = map[v][u] = lcm(u, v);
for (int k = 1; k <= N; k++)
for (int i = 1; i <= N; i++)
for (int j = 1; j <= N; j++)
map[i][j] = min(map[i][j], map[i][k] + map[k][j]);
printf("%d", map[1][N]);
}
#F 时间显示
时间限制: 1.0 s 1.0\mathrm s 1.0s 内存限制: 256.0 M B 256.0\mathrm{MB} 256.0MB 本题总分: 15 15 15 分
问题描述
小蓝要和朋友合作开发一个时间显示的网站。在服务器上,朋友已经获取了当前的时间,用一个整数表示,值为从
1970
1970
1970 年
1
1
1 月 1 日
00
:
00
:
00
00:00:00
00:00:00 到当前时刻经过的毫秒数。
现在,小蓝要在客户端显示出这个时间。小蓝不用显示出年月日,只需显示出时分秒即可,毫秒也不用显示,直接舍去即可。
给定一个用整数表示的时间,请将这个时间对应的时分秒输出。
输入格式
输入一行包含一个整数,表示时间。
输出格式
输出时分秒表示的当前时间,格式形如 H H HH HH: M M MM MM: S S SS SS,其中 H H HH HH 表示时,值为 0 0 0 到 23 23 23, M M MM MM 表示分,值为 0 0 0 到 59 59 59, S S SS SS 表示秒,值为 0 0 0 到 59 59 59。时、分、秒不足两位时补前导 0 0 0。
测试样例1
Input:
46800999
Output:
13:00:00
测试样例2
Input:
1618708103123
Output:
01:08:23
评测用例规模与约定
对于所有评测用例,给定的时间为不超过 1 0 18 10^{18} 1018 的正整数。
#include <stdio.h>
int main() {
long long n;
scanf("%lld", &n);
printf("%02d:%02d:%02d", n / 1000 / 60 / 60 % 24, n / 1000 / 60 % 60, n / 1000 % 60);
}
#G 砝码称重
时间限制: 1.0 s 1.0\mathrm s 1.0s 内存限制: 256.0 M B 256.0\mathrm{MB} 256.0MB 本题总分: 20 20 20 分
问题描述
你有一架天平和
N
N
N 个砝码,这
N
N
N 个砝码重量依次是
W
1
,
W
2
,
⋅
⋅
⋅
,
W
N
W_1, W_2, · · · , W_N
W1,W2,⋅⋅⋅,WN。
请你计算一共可以称出多少种不同的重量?
注意砝码可以放在天平两边。
输入格式
输入的第一行包含一个整数
N
N
N。
第二行包含
N
N
N 个整数:
W
1
,
W
2
,
W
3
,
⋯
,
W
N
W_1, W_2, W_3, \cdots , W_N
W1,W2,W3,⋯,WN。
输出格式
输出一个整数代表答案。
测试样例1
Input:
3
1 4 6
Output:
10
Explanation:
能称出的 10 种重量是:1、2、3、4、5、6、7、9、10、11。
1 = 1;
2 = 6 − 4 (天平一边放 6,另一边放 4);
3 = 4 − 1;
4 = 4;
5 = 6 − 1;
6 = 6;
7 = 1 + 6;
9 = 4 + 6 − 1;
10 = 4 + 6;
11 = 1 + 4 + 6。
评测用例规模与约定
对于
50
50
50% 的评测用例,
1
≤
N
≤
15
1 ≤ N ≤ 15
1≤N≤15。
对于所有评测用例,
1
≤
N
≤
100
1 ≤ N ≤ 100
1≤N≤100,
N
N
N 个砝码总重不超过
100000
100000
100000。
背包 DP
终于逮到一个 J a v a \mathrm{Java} Java 组没有的题,可惜是 F \mathrm F F 题,还是个简单背包。
不过依题意若干砝码做加减运算,得到的绝对值可以视为一个可以称出的重量,
如果我们将若干砝码可能加减出的结果放在数轴上,显然可以发现,它是以原点对称的,于是我们可以只对正整数部分做背包,然后将上一轮背包 ( 1 , w i ) (1,w_i) (1,wi) 部分的结果视为 ( − w i , − 1 ) (-w_i, -1) (−wi,−1),可以减少算法的常数。
#include <bits/stdc++.h>
using namespace std;
const int N = 100000;
bool dp[N + 1 << 1] = {1}, bf[N + 1 << 1] = {1};
int n, w, ans;
int main() {
cin >> n;
while (n--) {
cin >> w;
swap(dp, bf);
for (int i = N; i >= w; i--) dp[i] = bf[i] | bf[i - w] | bf[i + w];
for (int i = 1; i < w; i++) dp[i] |= bf[w - i];
}
for (int i = 1; i <= N; i++) if (dp[i]) ans++;
cout << ans << endl;
}
#H 杨辉三角形
时间限制: 1.0 s 1.0\mathrm s 1.0s 内存限制: 256.0 M B 256.0\mathrm{MB} 256.0MB 本题总分: 20 20 20 分
下面的图形是著名的杨辉三角形:
如果我们按从上到下、从左到右的顺序把所有数排成一列,可以得到如下数列:
1
,
1
,
1
,
1
,
2
,
1
,
1
,
3
,
3
,
1
,
1
,
4
,
6
,
4
,
1
,
⋯
1, 1, 1, 1, 2, 1, 1, 3, 3, 1, 1, 4, 6, 4, 1, \cdots
1,1,1,1,2,1,1,3,3,1,1,4,6,4,1,⋯
给定一个正整数
N
N
N,请你输出数列中第一次出现
N
N
N 是在第几个数?
输入格式
输入一个整数 N N N。
输出格式
输出一个整数代表答案。
测试样例1
Input:
6
Output:
13
评测用例规模与约定
对于
20
20
20% 的评测用例,
1
≤
N
≤
10
1 ≤ N ≤ 10
1≤N≤10;
对于所有评测用例,
1
≤
N
≤
1000000000
1 ≤ N ≤ 1000000000
1≤N≤1000000000。
#include <stdio.h>
typedef long long ll;
ll N, ans = 1;
ll min(ll a, ll b) { return a < b ? a : b; }
ll C(int n, int m) {
ll C = 1;
for (int i = 0; i < m; i++) {
C = C * (n - i) / (i + 1);
if (C > N) return C;
}
return C;
}
int main() {
scanf("%lld", &N);
if (N > 1) {
ans = N * (N + 1) / 2 + 2;
for (int m = 2; m < 16; m++) {
int start = m << 1, end = N;
while (start <= end) {
int n = start + end >> 1;
if (C(n, m) == N) {
ans = min(ans, (n + 1ll) * n / 2 + m + 1);
break;
}
if (C(n, m) > N) end = n - 1;
else start = n + 1;
}
}
}
printf("%lld", ans);
}
#I 双向排序
时间限制: 1.0 1.0 1.0s 内存限制: 256.0 256.0 256.0MB 本题总分: 25 25 25 分
问题描述
给定序列
(
a
1
,
a
2
,
⋅
⋅
⋅
,
a
n
)
=
(
1
,
2
,
⋅
⋅
⋅
,
n
)
(a_{1}, a_{2}, · · · , a_{n}) = (1, 2, · · · , n)
(a1,a2,⋅⋅⋅,an)=(1,2,⋅⋅⋅,n),即
a
i
=
i
a_{i} = i
ai=i。
小蓝将对这个序列进行
m
m
m 次操作,每次可能是将
a
1
,
a
2
,
⋅
⋅
⋅
,
a
q
i
a_{1}, a_{2}, · · · , a_{q_{i}}
a1,a2,⋅⋅⋅,aqi 降序排列,或者将
a
q
i
,
a
q
i
+
1
,
⋅
⋅
⋅
,
a
n
a_{q_{i}}, a_{q_{i+1}}, · · · , a_{n}
aqi,aqi+1,⋅⋅⋅,an 升序排列。
请求出操作完成后的序列。
输入格式
输入的第一行包含两个整数
n
,
m
n, m
n,m,分别表示序列的长度和操作次数。
接下来
m
m
m 行描述对序列的操作,其中第
i
i
i 行包含两个整数
p
i
,
q
i
p_{i}, q_{i}
pi,qi 表示操作类型和参数。当
p
i
=
0
p_{i} = 0
pi=0 时,表示将
a
1
,
a
2
,
⋅
⋅
⋅
,
a
q
i
a_{1}, a_{2}, · · · , a_{q_{i}}
a1,a2,⋅⋅⋅,aqi 降序排列;当
p
i
=
1
p_{i} = 1
pi=1 时,表示将
a
q
i
,
a
q
i
+
1
,
⋅
⋅
⋅
,
a
n
a_{q_{i}}, a_{q_{i+1}}, · · · , a_{n}
aqi,aqi+1,⋅⋅⋅,an 升序排列。
输出格式
输出一行,包含 n n n 个整数,相邻的整数之间使用一个空格分隔,表示操作完成后的序列。
测试样例1
Input:
3 3
0 3
1 2
0 2
Output:
3 1 2
Explanation:
原数列为 (1, 2, 3)。
第 1 步后为 (3, 2, 1)。
第 2 步后为 (3, 1, 2)。
第 3 步后为 (3, 1, 2)。与第 2 步操作后相同,因为前两个数已经是降序了。
评测用例规模与约定
对于
30
30
30% 的评测用例,
n
,
m
≤
1000
n, m ≤ 1000
n,m≤1000;
对于
60
60
60% 的评测用例,
n
,
m
≤
5000
n, m ≤ 5000
n,m≤5000;
对于所有评测用例,
1
≤
n
,
m
≤
100000
1 ≤ n, m ≤ 100000
1≤n,m≤100000,
0
≤
p
i
≤
1
0 ≤ p_{i} ≤ 1
0≤pi≤1,
1
≤
q
i
≤
n
1 ≤ q_{i} ≤ n
1≤qi≤n;
贪心
#include <stdio.h>
int n, m, p, q, top;
int P[100001], Q[100001], ans[100001];
int max(int arg1, int arg2) { return arg1 > arg2 ? arg1 : arg2; }
int min(int arg1, int arg2) { return arg1 < arg2 ? arg1 : arg2; }
int main() {
scanf("%d %d", &n, &m);
while (m--) {
scanf("%d %d", &p, &q);
if (p) {
while (top > 0 && P[top] == p) q = min(q, Q[top--]);
while (top > 1 && Q[top - 1] >= q) top -= 2;
} else {
while (top > 0 && P[top] == p) q = max(q, Q[top--]);
while (top > 1 && Q[top - 1] <= q) top -= 2;
}
P[++top] = p; Q[top] = q;
}
m = n, p = 0, q = n - 1;
for (int i = 1; i <= top; i++)
if (P[i]) while (p + 1 < Q[i] && p <= q) ans[p++] = m--;
else while (q >= Q[i] && p <= q) ans[q--] = m--;
if (top & 1) while (p <= q) ans[p++] = m--;
else while (p <= q) ans[q--] = m--;
for (int i = 0; i < n; i++)
printf("%d ", ans[i]);
}
Chtholly Tree
题解在这儿,这里就是打个珂朵莉树的板子给大伙看看。
比 J a v a \mathrm{Java} Java 好写多了。
#include <bits/stdc++.h>
#define NODESIZE 4000000
int n, cur, Left[NODESIZE], Right[NODESIZE], K[NODESIZE];
struct Root {
int L, R, P, rt;
Root(int l, int r = 0, int p = 0, int rot = 0) : L(l), R(r), P(p), rt(rot) {}
inline bool operator < (const Root &rot) const { return L < rot.L; }
};
typedef std::set<Root>::iterator iter;
std::set<Root> rts;
int build(int l, int r, int k) {
if (l == r) {
K[++cur] = 1;
return cur;
}
int node = ++cur, mid = l + r >> 1;
if (k <= mid) Left[node] = build(l, mid, k);
else Right[node] = build(mid + 1, r, k);
K[node] = K[Right[node]] + K[Left[node]];
return node;
}
void print(int l, int r, int p, int rt) {
if (rt) {
if (l == r) printf("%d ", l);
else {
int mid = l + r >> 1;
if (p) {
print(l, mid, p, Left[rt]);
print(mid + 1, r, p, Right[rt]);
} else {
print(mid + 1, r, p, Right[rt]);
print(l, mid, p, Left[rt]);
}
}
}
}
int split(int rt, int p, int k) {
if (rt == 0) return 0;
int node = ++cur;
if (p) {
if (k <= K[Left[rt]]) {
if (k != K[Left[rt]]) Left[node] = split(Left[rt], p, k);
std::swap(Right[node], Right[rt]);
} else Right[node] = split(Right[rt], p, k - K[Left[rt]]);
} else {
if (k <= K[Right[rt]]) {
if (k != K[Right[rt]]) Right[node] = split(Right[rt], p, k);
std::swap(Left[node], Left[rt]);
} else Left[node] = split(Left[rt], p, k - K[Right[rt]]);
}
K[node] = K[rt] - k;
K[rt] = k;
return node;
}
iter split(int x) {
if (x > n) return rts.end();
iter it = --rts.upper_bound(Root(x));
if (it -> L == x) return it;
int L = it -> L, R = it -> R, P = it -> P, rt = it -> rt;
rts.erase(it);
rts.insert(Root(L, x - 1, P, rt));
return rts.insert(Root(x, R, P, split(rt, P, x - L))).first;
}
int merge(int rt1, int rt2) {
if (!rt1) return rt2;
if (rt2) {
K[rt1] += K[rt2];
Left[rt1] = merge(Left[rt1], Left[rt2]);
Right[rt1] = merge(Right[rt1], Right[rt2]);
}
return rt1;
}
int main() {
int m, p, q;
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) rts.insert(Root(i, i, 1, build(1, n, i)));
while (m--) {
scanf("%d %d", &p, &q);
int node = ++cur;
if (p) {
iter r = split(n + 1), l = split(q);
for (iter i = l; i != r; ++i)
node = merge(node, i -> rt);
rts.erase(l, r);
rts.insert(Root(q, n, p, node));
} else {
iter r = split(q + 1), l = split(1);
for (iter i = l; i != r; ++i)
node = merge(node, i -> rt);
rts.erase(l, r);
rts.insert(Root(1, q, p, node));
}
}
for (Root rt : rts)
print(1, n, rt.P, rt.rt);
}
#J 括号序列
时间限制: 1.0 s 1.0\mathrm s 1.0s 内存限制: 256.0 M B 256.0\mathrm{MB} 256.0MB 本题总分: 25 25 25 分
问题描述
给定一个括号序列,要求尽可能少地添加若干括号使得括号序列变得合法,当添加完成后,会产生不同的添加结果,请问有多少种本质不同的添加结果。
两个结果是本质不同的是指存在某个位置一个结果是左括号,而另一个是右括号。
例如,对于括号序列 (((),只需要添加两个括号就能让其合法,有以下几种不同的添加结果:()()()、()(())、(())()、(()()) 和 ((()))。
输入格式
输入一行包含一个字符串 s s s,表示给定的括号序列,序列中只有左括号和右括号。
输出格式
输出一个整数表示答案,答案可能很大,请输出答案除以 1000000007 1000000007 1000000007 (即 1 0 9 + 7 10^{9} + 7 109+7) 的余数。
测试样例1
Input:
((()
Output:
5
评测用例规模与约定
对于
40
40
40% 的评测用例,
∣
s
∣
≤
200
|s| ≤ 200
∣s∣≤200。
对于所有评测用例,
1
≤
∣
s
∣
≤
5000
1 ≤ |s| ≤ 5000
1≤∣s∣≤5000。
#include <stdio.h>
#include <string.h>
typedef long long ll;
#define N 5555
int dp[N][N], p = 1000000007;
char str[N] = {'$'};
int calc(char *str) {
memset(dp, 0, sizeof dp);
dp[0][0] = 1;
int opening = 0, n = strlen(str) - 1;
for (int i = 1; i <= n; i++) {
if (str[i] == '(') {
for (int j = 1; j <= n; j++)
dp[i][j] = dp[i - 1][j - 1];
opening++;
}
else {
dp[i][0] = (dp[i - 1][0] + dp[i - 1][1]) % p;
for (int j = 1; j <= n; j++)
dp[i][j] = (dp[i][j - 1] + dp[i - 1][j + 1]) % p;
if (opening) opening--;
}
}
return dp[n][opening];
}
int reverse(char *str) {
for (int i = 1, j = strlen(str) - 1; i <= j; i++, j--) {
char temp = str[i] ^ 1;
str[i] = str[j] ^ 1;
str[j] = temp;
}
}
int main() {
scanf("%s", str + 1);
ll ans = calc(str);
reverse(str);
printf("%d\n", ans * calc(str) % p);
}