2025牛客暑期多校训练营4
B. Blind Alley
题意:在一个迷宫里,从\((1, 1)\)出发,只能向上向下和向右走,且不能走墙。目的是走到\((1, m)\)。如果走到了第\(j\)列,那么可以看到\([j, \min(j + k, m)]\)这些列的状态。求有没有一个位置,使得其不能到达终点,但从起点到这个点的路径上除了\((i, j)\)的所有点的视野都无法判断这个点不能到达终点。
记\(r[i][j]\)为\((i, j)\)可以到的最右边的列,那么如果\((i, j)\)可以从\((1, 1)\)到达,且\(r[i][j] \geq j + k\)。则可以走到\((i, j)\)这个位置,而不知道这个位置不能到终点。
\(r[i][j]\)可以预处理,从后往前\(dp\)就行。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n, m, k;
std::cin >> n >> m >> k;
std::vector<std::string> s(n);
for (int i = 0; i < n; ++ i) {
std::cin >> s[i];
}
std::vector r(n, std::vector<int>(m));
for (int i = 0; i < n; ++ i) {
r[i][m - 1] = m - 1;
}
for (int j = m - 2; j >= 0; -- j) {
for (int i = 0; i < n; ++ i) {
r[i][j] = j;
}
for (int i = 0; i < n; ++ i) {
if (s[i][j] == '0') {
r[i][j] = std::max(r[i][j], r[i][j + 1]);
}
}
for (int i = 1; i < n; ++ i) {
if (s[i][j] == '0') {
r[i][j] = std::max(r[i][j], r[i - 1][j]);
}
}
for (int i = n - 2; i >= 0; -- i) {
if (s[i][j] == '0') {
r[i][j] = std::max(r[i][j], r[i + 1][j]);
}
}
}
std::vector f(n, std::vector<int>(m));
for (int i = 0; i < n; ++ i) {
f[i][0] = 1;
}
for (int j = 1; j + k - 1 < m; ++ j) {
for (int i = 0; i < n; ++ i) {
if (s[i][j] == '0') {
f[i][j] |= f[i][j - 1];
}
}
for (int i = 1; i < n; ++ i) {
if (s[i][j] == '0') {
f[i][j] |= f[i - 1][j];
}
}
for (int i = n - 2; i >= 0; -- i) {
if (s[i][j] == '0') {
f[i][j] |= f[i + 1][j];
}
}
for (int i = 0; i < n; ++ i) {
if (s[i][j] == '0' && f[i][j] && r[i][j] >= j + k && r[i][j] < m - 1) {
std::cout << "Yes\n";
return;
}
}
}
std::cout << "No\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
D. Determinant of 01-Matrix
赛后补题
题意:构造一个行列式,使得它的值为\(n\)。
构造太难,而且线代早忘了。。。
参考题解,大意就是第一行作为符号,然后下面构造一些等于\(2\)和等于\(1\)的位置,就可以用二进制一位一位构造出\(n\)来。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
const int N = 29;
int a[210][210];
void solve() {
for (int i = 0; i < N; ++ i) {
a[4 * i + 1][4 * i] = 1;
a[4 * i + 1][4 * i + 1] = 1;
a[4 * i + 2][4 * i + 1] = 1;
a[4 * i + 2][4 * i + 2] = 1;
a[4 * i + 3][4 * i] = 1;
a[4 * i + 3][4 * i + 2] = 1;
a[4 * i + 3][4 * i + 3] = 1;
a[4 * i + 4][4 * i + 3] = 1;
a[4 * i + 4][4 * i + 4] = 1;
}
int n;
std::cin >> n;
for (int i = 0; i <= N; ++ i) {
if (n >> i & 1) {
a[0][4 * i] = 1;
}
}
std::cout << 4 * N + 1 << "\n";
for (int i = 0; i <= 4 * N; ++ i) {
for (int j = 0; j <= 4 * N; ++ j) {
std::cout << a[i][j] << " \n"[j == 4 * N];
}
}
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
// std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
E. Echoes of 24
题意:一棵树,每个点有权值。有两种操作,一种是单点修改权值,一种是查询一条路径上,依次对于每个数选择加或乘能否得到\(24\)这个数字。
如果两个点距离小于等于\(24\),直接把这条路径搞出来然后\(dp\)就行了。否则,我们发现如果不等于\(1\)的值的和小于等于\(24\),就一定可以,因为我们可以把这些数全加上,不够就继续加一,够了就乘一。那么我们需要知道树上路径的和,再加上有修改操作,可以用树链剖分加单点修改区间查询的数据结构维护。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
struct HLD {
std::vector<int> in, out, size, deep, fa, top, idx;
std::vector<std::vector<int>> adj;
int n, dfn;
HLD(){}
HLD(int _n) {
init(_n);
}
void init(int _n) {
n =_n;
in.resize(n);
out.resize(n);
size.resize(n);
deep.resize(n);
fa.resize(n);
top.resize(n);
idx.resize(n);
dfn = 0;
adj.assign(n, {});
}
void addEdge(int u, int v) {
adj[u].push_back(v);
adj[v].push_back(u);
}
void work(int root = 0) {
top[root] = root;
fa[root] = -1;
deep[root] = 0;
dfs1(root);
dfs2(root);
}
void dfs1(int u) {
if (fa[u] != -1) {
adj[u].erase(std::find(adj[u].begin(), adj[u].end(), fa[u]));
}
size[u] = 1;
for (int i = 0; i < adj[u].size(); ++ i) {
int v = adj[u][i];
fa[v] = u;
deep[v] = deep[u] + 1;
dfs1(v);
size[u] += size[v];
if (size[v] > size[adj[u][0]]) {
std::swap(adj[u][0], adj[u][i]);
}
}
}
void dfs2(int u) {
idx[dfn] = u;
in[u] = dfn ++ ;
for (auto & v : adj[u]) {
top[v] = v == adj[u][0] ? top[u] : v;
dfs2(v);
}
out[u] = dfn - 1;
}
int lca(int u, int v) {
while (top[u] != top[v]) {
if (deep[top[u]] < deep[top[v]]) {
std::swap(u, v);
}
u = fa[top[u]];
}
return deep[u] < deep[v] ? u : v;
}
int dist(int u, int v) {
return deep[u] + deep[v] - 2 * deep[lca(u, v)];
}
};
template <class Info>
struct SegmentTree {
struct Node {
int l, r;
Info info;
};
std::vector<Node> tr;
SegmentTree() {};
SegmentTree(int n) {
init(n);
}
SegmentTree(std::vector<Info> & info) {
init(info);
}
void init(int n) {
tr.assign(n << 2, {});
build(0, n - 1);
}
void init(std::vector<Info> & info) {
int n = info.size();
tr.assign(n << 2, {});
std::function<void(int, int, int)> build = [&](int l, int r, int u) -> void {
tr[u] = {l, r, {}};
if (l == r) {
tr[u].info = info[l];
return;
}
int mid = l + r >> 1;
build(l, mid, u << 1); build(mid + 1, r, u << 1 | 1);
pushup(u);
};
build(0, n - 1, 1);
}
void pushup(int u) {
tr[u].info = tr[u << 1].info + tr[u << 1 | 1].info;
}
void build(int l, int r, int u = 1) {
tr[u] = {l, r, {}};
if (l == r) {
return;
}
int mid = l + r >> 1;
build(l, mid, u << 1); build(mid + 1, r, u << 1 | 1);
pushup(u);
}
void modify(int p, const Info & info, bool set = false) {
int u = 1;
while (tr[u].l != tr[u].r) {
int mid = tr[u].l + tr[u].r >> 1;
if (p <= mid) {
u = u << 1;
} else {
u = u << 1 | 1;
}
}
if (set) {
tr[u].info = info;
} else {
tr[u].info = tr[u].info + info;
}
u >>= 1;
while (u) {
pushup(u);
u >>= 1;
}
}
Info query(int l, int r, int u = 1) {
if (l <= tr[u].l && tr[u].r <= r) {
return tr[u].info;
}
int mid = tr[u].l + tr[u].r >> 1;
if (r <= mid) {
return query(l, r, u << 1);
} else if (l > mid) {
return query(l, r, u << 1 | 1);
}
return query(l, r, u << 1) + query(l, r, u << 1 | 1);
}
};
struct Info {
int sum;
};
Info operator + (const Info & l, const Info & r) {
Info res{};
res.sum = l.sum + r.sum;
return res;
}
void solve() {
int n, q;
std::cin >> n >> q;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
HLD tr(n);
for (int i = 1; i < n; ++ i) {
int u, v;
std::cin >> u >> v;
-- u, -- v;
tr.addEdge(u, v);
}
tr.work();
std::vector<Info> info(n);
for (int i = 0; i < n; ++ i) {
info[tr.in[i]].sum = a[i] > 1 ? std::min(25, a[i]) : 0;
}
SegmentTree<Info> segtr(info);
auto sum = [&](int u, int v) -> int {
Info res{};
while (tr.top[u] != tr.top[v]) {
if (tr.deep[tr.top[u]] < tr.deep[tr.top[v]]) {
std::swap(u, v);
}
res = res + segtr.query(tr.in[tr.top[u]], tr.in[u]);
u = tr.fa[tr.top[u]];
}
if (tr.deep[u] > tr.deep[v]) {
std::swap(u, v);
}
res = res + segtr.query(tr.in[u], tr.in[v]);
return res.sum;
};
auto dp = [&](int u, int v) -> int {
int fa = tr.lca(u, v);
std::vector<int> b, c;
while (u != fa) {
b.push_back(u);
u = tr.fa[u];
}
b.push_back(fa);
while (v != fa) {
c.push_back(v);
v = tr.fa[v];
}
b.insert(b.end(), c.rbegin(), c.rend());
if (a[b[0]] > 24) {
return 0;
}
std::array<bool, 25> f{};
f[a[b[0]]] = true;
b.erase(b.begin());
for (auto & x : b) {
std::array<bool, 25> g{};
for (int i = 1; i <= 24; ++ i) {
if (i + a[x] <= 24) {
g[i + a[x]] |= f[i];
}
if (1LL * i * a[x] <= 24) {
g[i * a[x]] |= f[i];
}
}
f = g;
}
return f[24];
};
while (q -- ) {
int op, x, y;
std::cin >> op >> x >> y;
if (op == 1) {
-- x, -- y;
if (tr.dist(x, y) > 24) {
std::cout << (sum(x, y) <= 24) << "\n";
} else {
std::cout << dp(x, y) << "\n";
}
} else {
-- x;
a[x] = y;
segtr.modify(tr.in[x], Info{y == 1 ? 0 : std::min(25, y)}, true);
}
}
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
// std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
F. For the Treasury!
题意:一个数组取前\(k\)个数的和为价值。你每次可以移动相邻两个数,代价为\(c\),求可以得到的最大价值。
如果想让第\(i\)个数移动后面去,让\(j\)从后面进来,可以看作\(j\)移动到\(k+1\),然后\(i\)也移动到\(k+1\)。那么记原本价值为\(sum\),对于\(i\leq k\),移动第\(i\)个数代价为\(a[i] + (k-i+1)\times c\);对于\(i>k\),贡献为\(a[i] - (i-k+1)\times c\)。我们每次选一对交换,选前面代价最小的和后面贡献最大的。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n, k;
i64 c;
std::cin >> n >> k >> c;
std::vector<i64> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::vector<i64> A, B;
i64 ans = std::accumulate(a.begin(), a.begin() + k, 0ll);
for (int i = 0; i < k; ++ i) {
A.push_back(a[i] + (k - i) * c);
}
for (int i = k; i < n; ++ i) {
B.push_back(a[i] - (i - k) * c);
}
std::ranges::sort(A);
std::ranges::sort(B, std::greater<>());
i64 sum = ans;
for (int i = 0; i < A.size() && i < B.size(); ++ i) {
sum += -A[i] + B[i];
ans = std::max(ans, sum);
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
// std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
G. Ghost in the Parentheses
赛后补题。
题意:一个括号序列,每个位置可能变成未知的,对于未知的地方随便填,求有多少情况只能填出来原序列这一种合法的括号序列。
经典技巧是把'('看作\(1\),')'看作\(-1\),那么一个合法括号序列的充要条件是,每一个前缀都大于等于\(0\)且总和为\(0\)。
考虑有哪些位置未知只能填出原序列。
满足条件的序列有两个条件:
- 左边若干个'('和右边若干个')'组成
- 交换最后的'('和最前的')'会使得没有括号序列合法。
如果不满足条件一,则交换一个')'和在它左边的'(',只会使得一些前缀和变大,那么得到的序列依然合法,也就是可以得到其它合法的序列。
考虑条件二的情况是\(((((...))))\)的情况,只要交换最后的')'和最前的'('非法,则只能填出原序列。因为拿一个更前的'('来交换,影响的前缀更多,更容易出现不合法的情况,而我们考虑的是有没有一种方式导致这个序列合法,也就是会有导致合法序列出现,那么希望观察影响前缀最小的情况,如果这个情况也无法产生其它的合法序列,则只能填出来原序列。
有人可能不懂为什么要交换两个括号,因为我们希望选出来的序列随便填也只能得到原序列一种,那么显然左括号右括号个数和原来不一样的填法肯定不合法,那么如果个数还是和原来一样,就可以看作有些括号进行了交换,显然交换相同括号没有意义,所以我们讨论交换左右括号的贡献。
那么对于交换的一对\(i, j\),\(i\)的位置本来贡献是\(1\),现在贡献是\(-1\),那么\([i, j)\)这一段的前缀和都要减\(2\),则只需要存在一个\(k\in [i, j)\),满足\(sum[k] < 2\)就行。
实际实现中,我们只需要枚举\(k\)就行了,那么记\(pre, suf\)分别为前缀左括号个数和后缀右括号个数,则有\(2^{pre_k} \times 2^{suf_{k+1}}\)中选法,但发现不同的\(k\)会重复计算一些区间,可以记\(pre\_cnt\)为上一个\(k\)的左括号的个数,\(now\)为上一个\(k\)到当前\(k\)之间的左括号的个数,然后我们不计算全不选的情况,贡献为\(2^{pre\_cnt} \times (2^{now}- 1) \times 2^{suf_{k+1}}\)。最后我们这里是没有计算全是右括号的情况的,以及需要加上一个不选的情况,那么需要再加上\(2^{\frac{n}{2}}\)。
代码省略取模类。
点击查看代码
constexpr int P = 998244353;
using Z = MInt<P>;
void solve() {
std::string s;
std::cin >> s;
int n = s.size();
std::vector<int> sum(n + 1);
for (int i = 0; i < n; ++ i) {
sum[i + 1] = sum[i] + (s[i] == '(' ? 1 : -1);
}
std::vector<int> suf(n + 2);
for (int i = n - 1; i >= 0; -- i) {
suf[i + 1] = suf[i + 2] + (s[i] == ')');
}
std::vector<Z> p(n + 1);
p[0] = 1;
for (int i = 1; i <= n; ++ i) {
p[i] = p[i - 1] * 2;
}
Z ans = 0;
int pre = 0, now = 0;
for (int i = 1; i <= n; ++ i) {
now += s[i - 1] == '(';
if (sum[i] < 2) {
ans += p[pre] * (p[now] - 1) * p[suf[i + 1]];
pre += now;
now = 0;
}
}
ans += p[n / 2];
std::cout << ans / p[n] << "\n";
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
// std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
I. I, Box
题意:推箱子,不过箱子可以自己左右移动。
无解的情况就是一个联通块里箱子和目标个数不一样。
然后就是暴力模拟了,我是用三个\(set\)分别存没到目标的箱子的位置、没有箱子的目标位置、有箱子的目标位置。
然后每次从第二个集合里拿一个\(bfs\)找第一个集合里最近的。记录路径然后模拟,如果遇到第三个集合的位置,则总体往前推,再从最后一个箱子继续推。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
struct DSU {
std::vector<int> fa, cnt;
DSU();
DSU(int n) {
init(n);
}
void init(int n) {
fa.assign(n + 1, 0);
cnt.assign(n + 1, 1);
std::ranges::iota(fa, 0);
}
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
bool merge(int x, int y) {
int u = find(x), v = find(y);
if (u == v) {
return false;
}
fa[v] = u;
cnt[u] += cnt[v];
return true;
}
bool same(int u, int v) {
return find(u) == find(v);
}
int size(int u) {
return cnt[find(u)];
}
};
const int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int st[55][55];
std::pair<int, int> pre[55][55];
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<std::string> s(n);
for (int i = 0; i < n; ++ i) {
std::cin >> s[i];
}
std::set<std::pair<int, int>> a, b, c;
DSU dsu(n * m);
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < m; ++ j) {
if (s[i][j] == '@') {
a.emplace(i, j);
} else if (s[i][j] == '*') {
b.emplace(i, j);
} else if (s[i][j] == '!') {
c.emplace(i, j);
}
if (s[i][j] == '#') {
continue;
}
for (int k = 0; k < 4; ++ k) {
int x = i + dx[k], y = j + dy[k];
if (x < 0 || x >= n || y < 0 || y >= m || s[x][y] == '#') {
continue;
}
dsu.merge(i * m + j, x * m + y);
}
}
}
if (a.empty()) {
std::cout << 0 << "\n";
return;
}
{
auto get = [&](const std::pair<int, int> & a) -> int {
return a.first * m + a.second;
};
std::vector<int> cnta(n * m), cntb(n * m);
for (auto & it : a) {
++ cnta[dsu.find(get(it))];
}
for (auto & it : b) {
++ cntb[dsu.find(get(it))];
}
for (int i = 0; i < n * m; ++ i) {
if (cnta[i] != cntb[i]) {
std::cout << -1 << "\n";
return;
}
}
}
std::vector<std::tuple<int, int, char>> ans;
auto work = [&](int sx, int sy) -> void {
std::queue<std::pair<int, int>> q;
memset(st, 0, sizeof st);
pre[sx][sy] = {-1, -1};
st[sx][sy] = 1;
q.emplace(sx, sy);
int tx, ty;
while (q.size()) {
auto [x, y] = q.front(); q.pop();
if (a.count({x, y})) {
tx = x, ty = y;
break;
}
for (int i = 0; i < 4; ++ i) {
int nx = x + dx[i], ny = y + dy[i];
if (nx < 0 || nx >= n || ny < 0 || ny >= m || s[nx][ny] == '#' || st[nx][ny]) {
continue;
}
pre[nx][ny] = {x, y};
st[nx][ny] = 1;
q.emplace(nx, ny);
}
}
std::vector<std::pair<int, int>> path;
a.erase({tx, ty});
while (tx != sx || ty != sy) {
path.emplace_back(tx, ty);
auto & [nx, ny] = pre[tx][ty];
tx = nx;
ty = ny;
}
path.emplace_back(sx, sy);
auto getop = [&](const std::pair<int, int> & a, const std::pair<int, int> & b) -> char {
int x = a.first - b.first, y = a.second - b.second;
if (x == -1 && y == 0) {
return 'D';
} else if (x == 1 && y == 0) {
return 'U';
} else if (x == 0 && y == -1) {
return 'R';
} else {
return 'L';
}
};
for (int i = 0; i + 1 < path.size(); ++ i) {
if (c.count(path[i + 1])) {
int j = i + 1;
while (c.count(path[j + 1])) {
++ j;
}
for (int k = j; k >= i; -- k) {
ans.emplace_back(path[k].first, path[k].second, getop(path[k], path[k + 1]));
}
i = j;
} else {
ans.emplace_back(path[i].first, path[i].second, getop(path[i], path[i + 1]));
}
}
c.emplace(sx, sy);
b.erase({sx, sy});
};
while (b.size()) {
auto & [x, y] = *b.begin();
work(x, y);
}
std::cout << ans.size() << "\n";
for (auto & [x, y, op] : ans) {
std::cout << x + 1 << " " << y + 1 << " " << op << "\n";
}
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
// std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}
L. Ladder Challenge
题意:给你一个长度为\(n\)的数组,有\(Q\)次询问,每次给你初始值\(x\),你需要从小到大顺序一个一个数挑战,如果\(a_i\)小于等于\(x\)则跳到选一个数,否则一直使得\(x\)加一,\(a_i\)减一,直到\(x\)大于等于\(a_i\),目的是成为第\(y\)大的数。求需要操作多少次。
赛后补题。
思路来自\(jiangly\)代码。
\(x\)和\(a_i\)操作后会变成\(\lceil \frac{x+a_i}{2} \rceil\),那么因为\(a_i, x \leq 1e9\),则最多\(30\)次\(x\)的贡献就变的可有可无。也就是不同的\(x\)从\(i\)出发,经过三十个数后相差最多为\(1\)。那么我们可以把询问离线,用\(map\)存所有不同的数,然后模拟。(这个做法实在是太简单了,只能说哥哥太强了)。
代码也是参考了\(jiangly\)的。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
struct DSU {
std::vector<int> fa, cnt;
DSU();
DSU(int n) {
init(n);
}
void init(int n) {
fa.assign(n + 1, 0);
cnt.assign(n + 1, 1);
std::ranges::iota(fa, 0);
}
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
bool merge(int x, int y) {
int u = find(x), v = find(y);
if (u == v) {
return false;
}
fa[v] = u;
cnt[u] += cnt[v];
return true;
}
bool same(int u, int v) {
return find(u) == find(v);
}
int size(int u) {
return cnt[find(u)];
}
};
void solve() {
int n, q;
std::cin >> n >> q;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::vector<std::vector<std::pair<int, int>>> add(n), del(n);
for (int i = 0; i < q; ++ i) {
int x, y;
std::cin >> x >> y;
int l = std::ranges::upper_bound(a, x) - a.begin();
int r = n - y;
if (l > r) {
continue;
}
add[l].emplace_back(x, i);
del[r].emplace_back(x, i);
}
std::vector<int> val(q), ans(q);
DSU dsu(q);
std::map<int, int> f;
for (int i = 0; i < n; ++ i) {
for (auto & [x, id] : add[i]) {
if (f.contains(x)) {
dsu.merge(f[x], id);
} else {
f[x] = id;
val[id] = x;
}
}
std::map<int, int> nf;
for (auto & [x, id] : f) {
int nx = (x + a[i] + 1) / 2;
if (nf.contains(nx)) {
dsu.merge(nf[nx], id);
} else {
nf[nx] = id;
val[id] = nx;
}
}
f = nf;
for (auto & [x, id] : del[i]) {
ans[id] = val[dsu.find(id)] - x;
}
}
for (int i = 0; i < q; ++ i) {
std::cout << ans[i] << "\n";
}
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t = 1;
// std::cin >> t;
while (t -- ) {
solve();
}
return 0;
}