2025牛客寒假算法基础集训营3
A. 智乃的博弈游戏
题意:两个人轮流拿石头,拿的数量必须和总数互质,轮到某个玩家时只剩下一颗石头就算他赢。问先手能不能赢。
如果\(n>1\)并且是奇数可以拿走\(n-2\)个,这样可以看出来奇数必赢。如果是偶数,那么我们只能拿一个奇数,偶数减奇数等于奇数,后手必赢。
点击查看代码
void solve() {
i64 n;
std::cin >> n;
if (n & 1) {
std::cout << "Yes\n";
} else {
std::cout << "No\n";
}
}
B. 智乃的质数手串
题意:\(n\)个数,首尾相接。你每次可以选择两个相邻的数,如果它们的和是质数,那么可以删除前面那个数。如果只有一个数了可以直接删除。求一种方案删除所有数。
考虑不是环而是一条链该怎么做。从左到右看,如果有个数可以删那就直接删,不删的话也不能让它删去右边的数,留着反而没用。那么可以用一个栈来模拟,贪心的删除每个数。
考虑是环该怎么做。经典的破环成链,把数组复制一份到后面,然后用双端队列操作,每次把和当前位置距离已经超过\(n\)的位置出队,保证队列里存的每个位置都是当前可以操作的。那么当某个时刻队列为空,就找到了以当前元素结尾的一个合法数组。
点击查看代码
const int N = 2e5 + 5;
std::vector<int> primes;
bool st[N];
void init(int n) {
for (int i = 2; i <= n; ++ i) {
if (!st[i]) {
primes.push_back(i);
}
for (auto & p : primes) {
if (p * i > n) {
break;
}
st[p * i] = true;
if (i % p == 0) {
break;
}
}
}
}
void solve() {
init(2e5);
int n;
std::cin >> n;
std::vector<int> a(2 * n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
a[i + n] = a[i];
}
std::deque<int> dq;
int pos = -1;
for (int i = 0; i < 2 * n; ++ i) {
while (dq.size() && i - dq.front() >= n) {
dq.pop_front();
}
while (dq.size() && !st[a[i] + a[dq.back()]]) {
dq.pop_back();
}
if (i >= n - 1 && dq.empty()) {
pos = i - n + 1;
break;
}
dq.push_back(i);
}
if (pos == -1) {
std::cout << "No\n";
} else {
std::cout << "Yes\n";
std::vector<int> ans;
std::stack<int> stk;
for (int i = pos; i < pos + n; ++ i) {
while (stk.size() && !st[a[i] + a[stk.top()]]) {
ans.push_back(stk.top());
stk.pop();
}
stk.push(i);
}
ans.push_back(stk.top());
for (int i = 0; i < n; ++ i) {
std::cout << ans[i] % n << " \n"[i == n - 1];
}
}
}
C. 智乃的Notepad(Easy version) && D. 智乃的Notepad(Hard version)
题意:你要输入\(n\)个字符串,你除了输入26个字母之外还可以退格。问至少敲多少次键盘可以让\(n\)个字符串都出现过。
显然我们应该让尽可能多的字符串共用前缀。模拟一棵字典树,可以发现我们按照字典树的\(dfs\)序走就是一种方案,发现哪个方案走到次数都一样,但最后一个字符串可以不删,那就相当于我们最后要停在字典树最深的一个分支上,这样答案就是\(2\times\)字典树的总节点数减最大深度。其中最大深度就是最长的字符串长度,可以用\(st\)求出区间的最大长度,考虑怎么求区间的字典树总节点数。每个字符串插入的时候会经过一些点,我们把每个字符串经过点的编号存下来,那么问题就变成了求区间每个字符串所有经过的点里有多少个不同的点。这个问题和HH的项链几乎一样,HH的项链是每个位置是一个数,求区间不同数的个数,这个题是每个位置有很多数,求区间不同数的个数,类似的求法,存每个数上一次出现的位置,用树状数组维护。
点击查看代码
template <class Info>
struct ST {
std::vector<std::vector<Info>> st;
ST(std::vector<Info> a) {
int n = a.size(), m = std::__lg(n) + 1;
st.assign(n, std::vector<Info>(m));
for (int i = 0; i < n; ++ i) {
st[i][0] = a[i];
}
for (int j = 1; j < m; ++ j) {
for (int i = 0; i + (1 << j - 1) < n; ++ i) {
st[i][j] = st[i][j - 1] + st[i + (1 << j - 1)][j - 1];
}
}
}
Info query(int l, int r) {
int lg = std::__lg(r - l + 1);
return st[l][lg] + st[r - (1 << lg) + 1][lg];
}
};
struct Info {
int max;
};
Info operator + (Info a, Info b) {
return {std::max(a.max, b.max)};
}
template <class T>
struct Fenwick {
int n;
std::vector<T> tr;
Fenwick(int _n) {
init(_n);
}
void init(int _n) {
n = _n;
tr.assign(_n + 1, T{});
}
void add(int x, const T &v) {
for (int i = x; i <= n; i += i & -i) {
tr[i] = tr[i] + v;
}
}
T query(int x) {
T res = 0;
for (int i = x; i; i -= i & -i) {
res = res + tr[i];
}
return res;
}
T sum(int l, int r) {
return query(r) - query(l - 1);
}
};
const int N = 1e6 + 5;
std::vector<std::vector<int>> g(N);
int last[N];
const int TrieN = 1e6 + 5;
int trie[TrieN][26];
struct Trie {
int idx;
Trie(int n) {
idx = 0;
}
int newNode() {
idx += 1;
memset(trie[idx], 0, sizeof trie[idx]);
return idx;
}
void insert(std::string s, int id) {
int p = 0;
for (auto & c : s) {
int x = c - 'a';
if (!trie[p][x]) {
trie[p][x] = newNode();
}
p = trie[p][x];
g[id].push_back(p);
}
}
};
void solve() {
int n, m;
std::cin >> n >> m;
int sum = 0;
std::vector<std::string> s(n + 1);
std::vector<Info> len(n + 1);
for (int i = 1; i <= n; ++ i) {
std::cin >> s[i];
len[i].max = s[i].size();
sum += s[i].size();
}
ST<Info> st(len);
Trie tr(sum);
for (int i = 1; i <= n; ++ i) {
tr.insert(s[i], i);
}
std::vector<std::vector<std::pair<int, int>>> Q(n + 1);
for (int i = 0; i < m; ++ i) {
int l, r;
std::cin >> l >> r;
Q[r].push_back({l, i});
}
Fenwick<int> tr1(n + 1);
std::vector<int> ans(m);
for (int i = 1; i <= n; ++ i) {
for (auto & j : g[i]) {
if (last[j]) {
tr1.add(last[j], -1);
}
tr1.add(i, 1);
last[j] = i;
}
for (auto & [j, id] : Q[i]) {
ans[id] = 2 * tr1.sum(j, i) - st.query(j, i).max;
}
}
for (int i = 0; i < m; ++ i) {
std::cout << ans[i] << "\n";
}
}
E.智乃的小球
题意:\(n\)个小球在一个轴上,每个球的速度都是固定的,方向是左或右。如果有两个小球碰撞就会交换方向。问发生\(k\)次碰撞的时候是在多少秒。
两个球碰撞后可以看作是互相穿过了对方。因为它们交换了方向,然后他们的位置又是相邻的,完全可以看作两个小球都往自己的方向前进了。
那么我们可以二分时间,看每个球能和多少球相撞。
点击查看代码
void solve() {
int n, k;
std::cin >> n >> k;
std::vector<i64> a, b;
for (int i = 0; i < n; ++ i) {
i64 p, v;
std::cin >> p >> v;
if (v == 1) {
a.push_back(p);
} else {
b.push_back(p);
}
}
std::sort(a.begin(), a.end());
std::sort(b.begin(), b.end());
n = a.size();
int m = b.size();
auto check = [&](double t) -> i64 {
i64 cnt = 0;
for (int i = 0, l = 0, r = 0; i < n; ++ i) {
while (l < m && b[l] < a[i]) {
++ l;
}
r = std::max(l, r);
while (r < m && b[r] <= a[i] + 2 * t) {
++ r;
}
cnt += r - l;
}
return cnt;
};
double l = 1, r = 1e9;
while (r - l > 1e-6) {
double mid = (l + r) / 2;
// std::cerr << mid << "\n";
if (check(mid) >= k) {
r = mid;
} else {
l = mid;
}
}
std::cout << std::fixed << std::setprecision(12);
if (check(r) < k) {
std::cout << "No\n";
return;
}
std::cout << "Yes\n";
std::cout << r << "\n";
}
F. 智乃的捉迷藏
题意:有六个位置,三个人看着。其中每个人都有一个位置只能自己看到。其他三个位置都会被某两个人看到。总共有\(n\)个人在这六个位置里,选择告诉你每个人看见了多少人,问这个情况有没有可能。
在能被两个位置看到的人会被算多次。那么人数最少为所有人都不在会在能被两个人看见的位置上,那么就是\(a+b+c\)个人。最多的情况就是每个人都在能被两个位置看到的人的位置上,那么每个人会被算两次。就是\(2(a+b+c)\)。只要\(n\)在这个范围内就有解。
点击查看代码
void solve() {
int n, a, b, c;
std::cin >> n >> a >> b >> c;
if (a + b + c < n || a + b + c > n * 2) {
std::cout << "No\n";
} else {
std::cout << "Yes\n";
}
}
G. 智乃与模数
题意:求所有的\(n \% i(1 \leq i \leq n)\)的前\(k\)大和。
对于一个区间\([l, r]\),都有\(i \in [l, r], \lfloor \frac{n}{l} \rfloor = \lfloor \frac{n}{i} \rfloor = \lfloor \frac{n}{r} \rfloor\),那么我们发现他们的\(n \% i\)的值是一段等差数列,首项\(a\)为\(n \% l\),公差\(d\)为\(\frac{n}{l}\),第\(i\)项为\(a - (i - 1)d\)。(这种结论第一次遇到难以推出来,不过现在遇到了就一定要记下来)。
那么我们可以二分找前\(k\)大的数是哪些,每次遍历这些等差数列,看大于等于\(mid\)的数量是不是大于等于\(k\)。如果有\(k\)个了,那么我们的\(mid\)可以增大,否则减小。枚举每一个\([l, r]\)可以用数论分块来做。
这题思路大致就是这样,不过二分还是有点小细节,可能没有一个\(mid\)使得大于等于\(mid\)的数恰好有\(k\)个,那么如果我们二分第一个个数小于等于\(k\)的\(mid\),那么可能我们统计的数不足\(k\)个,于是我们需要加上\(k - cnt\)个\(mid-1\)。其中\(cnt\)是大于等于\(mid\)的个数,一定有这么多个\(mid-1\),不然就会有大于等于\(mid-1\)的数个数小于等于\(k\)个,与我们二分出来的答案矛盾。
点击查看代码
void solve() {
i64 n, k;
std::cin >> n >> k;
auto check = [&](int x) -> std::pair<int, i64> {
int cnt = 0;
i64 sum = 0;
for (int l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
int a = n % l, d = n / l;
if (a < x) {
continue;
}
//a - (i - 1) * d >= x
//i <= (a - x) / d + 1
i64 len = std::min(r - l + 1, (a - x) / d + 1);
cnt += len;
sum += len * (a + a - (len - 1) * d) / 2;
}
return {cnt, sum};
};
int l = 1, r = n / 2;
while (l < r) {
int mid = l + r >> 1;
if (check(mid).first <= k) {
r = mid;
} else {
l = mid + 1;
}
}
auto [cnt, sum] = check(l);
i64 ans = sum + (i64)(k - cnt) * (l - 1);
std::cout << ans << "\n";
}
H. 智乃与黑白树
题意:给你一棵树,每个点要么是黑的要么是白的。问所有是黑色开头白色结尾的路径长度之和。
这题的讨论还是有点麻烦,出题人的写法很好,以前没见过,感觉让换根dp变的好理解了,这里借鉴一下。
记\(f_u\)为\(u\)这颗子树的贡献,\(cnt_{u_{0/1}}\)为\(u\)这棵子树里\(0/1\)的数量,\(sum_{u_{0/1}}\)为\(u\)这棵子树里黑色点/白色点到\(u\)的距离之和。
如果已经算出来了以\(u\)为根的一些子树的贡献,选择要把一棵子树\(v\)接到\(u\)上,那么每个\(u\)里的黑点都要去\(v\)的白点,白点要去\(v\)里的黑点,发现他们都要经过点\(u\),那么我们把这些路径分成两部。以黑点举例,黑点到\(u\)的距离为\(sum_{u_0}\),这些距离要算上\(cnt_{v_1}\)遍,那么就是\(sum_{u_0} \times cnt_{v_1}\)。然后看从\(u\)到左边的白点要走多少,左边白点到\(v\)的距离已经有了,那么他们要到\(u\)只需要加1,因为每个白点都要算,所以距离为\(sum_{v_1} + cnt_{v_1}\),这个距离要算\(cnt_{u_0}\)次。白点到黑点的计算同理。于是总的距离就是\(f_u = f_v + sum_{u_0} \times cnt_{v_1} + cnt_{u_0} \times (sum_{v_1} + cnt_{v_1}) + sum_{u_1} \times cnt_{v_0} + cnt_{u_1} \times (sum_{v_0} + cnt_{v_0})\)。
\(sum\)和\(cnt\)的转移则好讨论。我们可以把这个写成一个函数\(link\),\(link(u, v)\)表示把\(v\)接到\(u\)上这样我们换根的时候就更直观更好写。然后再写一个\(cut\)函数,撤销之前的\(link\)。那么换根操作中,我们就可以先用\(cut\)把\(v\)对\(u\)的贡献脱离出来,然后再\(link(v, u)\)把\(u\)接到\(v\)上。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::string s;
std::cin >> s;
std::vector<std::vector<std::pair<int, int> > > adj(n);
std::vector<std::pair<int, int> > edges(n);
for (int i = 1; i < n; ++ i) {
int u, v;
std::cin >> u >> v;
-- u, -- v;
adj[u].push_back({i, v});
adj[v].push_back({i, u});
edges[i] = {u, v};
}
std::vector cnt(n, std::array<i64, 2>{});
std::vector sum(n, std::array<i64, 2>{});
std::vector<i64> f(n);
auto link = [&](int u, int v) -> void {
f[u] += f[v] + sum[u][0] * cnt[v][1] + sum[u][1] * cnt[v][0] +
cnt[u][0] * (sum[v][1] + cnt[v][1]) + cnt[u][1] * (sum[v][0] + cnt[v][0]);
sum[u][0] += sum[v][0] + cnt[v][0];
sum[u][1] += sum[v][1] + cnt[v][1];
cnt[u][0] += cnt[v][0];
cnt[u][1] += cnt[v][1];
};
auto cut = [&](int u, int v) -> void {
cnt[u][1] -= cnt[v][1];
cnt[u][0] -= cnt[v][0];
sum[u][1] -= sum[v][1] + cnt[v][1];
sum[u][0] -= sum[v][0] + cnt[v][0];
f[u] -= f[v] + sum[u][0] * cnt[v][1] + sum[u][1] * cnt[v][0] +
cnt[u][0] * (sum[v][1] + cnt[v][1]) + cnt[u][1] * (sum[v][0] + cnt[v][0]);
};
auto dfs = [&](auto self, int u, int fa) -> void {
int x = s[u] == 'b';
cnt[u][x] = 1;
for (auto & [_, v] : adj[u]) {
if (v == fa) {
continue;
}
self(self, v, u);
link(u, v);
}
};
dfs(dfs, 0, -1);
std::vector<std::pair<i64, i64> > ans(n);
auto dfs1 = [&](auto self, int u, int fa) -> void {
for (auto & [id, v] : adj[u]) {
if (v == fa) {
continue;
}
cut(u, v);
if (u == edges[id].first) {
ans[id] = {f[u], f[v]};
} else {
ans[id] = {f[v], f[u]};
}
link(v, u);
self(self, v, u);
cut(v, u);
link(u, v);
}
};
dfs1(dfs1, 0, -1);
for (int i = 1; i < n; ++ i) {
std::cout << ans[i].first << " " << ans[i].second << "\n";
}
}
I. 智乃的兔子跳
题意:给你\(n\)个数,你要选一个\(p\)和一个\(k\),使得\(p + ik\)包含尽可能多的数,\(i\)是一个非负整数。
以前没有接触过这种随机数的题。
\(k = 2\)的时候,我们可以选所有奇数或者偶数,至少可以选\(\frac{n}{2}\)个点。那么答案可以选的点肯定也大于等于\(\frac{n}{2}\),所以一个点在答案里的概率大于\(\frac{1}{2}\),两个点同时在一个答案里的概率大于\(\frac{1}{4}\)。那么我们可以多次随机两个点,枚举这两个点可能产生的\(k\),如果这两个同时在答案里,那么我们就可以枚举到答案。我们枚举一百次,那么枚举不到答案的概率极小极小。
点击查看代码
std::mt19937 gen(std::random_device{}());
int rand(int l, int r) {
std::uniform_int_distribution<int> dis(l, r);
return dis(gen);
}
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
if (n == 1) {
std::cout << a[0] << " " << 2 << "\n";
return;
}
auto get = [&](int p, int d) -> std::pair<int, int> {
int cnt = 0;
for (auto & x : a) {
if ((x - p) % d == 0) {
++ cnt;
}
}
return {cnt, p % d};
};
int ans = 2;
int p = 0, max = 0;
int t = 100;
while (t -- ) {
int i = rand(0, n - 1), j;
do {
j = rand(0, n - 1);
} while (i == j);
int k = std::abs(a[i] - a[j]);
if (k == 1) {
continue;
}
for (int d = 2; d * d <= k; ++ d) {
if (k % d == 0) {
while (k % d == 0) {
k /= d;
}
auto [cnt, pos] = get(a[i], d);
if (cnt > max) {
max = cnt;
p = pos;
ans = d;
}
}
}
if (k == 1) {
continue;
}
auto [cnt, pos] = get(a[i], k);
if (cnt > max) {
max = cnt;
p = pos;
ans = k;
}
}
std::cout << p << " " << ans << "\n";
}
J. 智乃画二叉树
题意:按要求画一棵二叉树。
感觉这题对我来说并不算难,写过蓝书上的搜索章节题目后,对于这种题还是得心应手的。不过赛时没看这题。。。
先算出最大高度和宽度,都是三百八十几,那么我们开个\(400 \times 400\)的字符数组就够了。先把所有字符都赋值为空格。如何\(dfs\)去画这棵树,根的位置可以取\(395,200\),这个位置是六边形左上角的位置。然后左右节点的坐标就可以直接算出来,直接\(dfs\)就行。现在考虑画边,就是分别从六边形的左下角和右下角出发,一直斜着往下直到儿子的高度就行了。最后的边框就是找到不等于空格的位置的坐标的最高最低最左最右的线就行了。
点击查看代码
const int N = 400;
char a[N][N];
int son[100][2];
int in[100];
int len[10];
void draw_node(int h, int w, int u) {
a[h][w] = a[h][w + 1] = '_';
a[h - 1][w - 1] = '/'; a[h - 1][w + 2] = '\\';
if (u < 10) {
a[h - 1][w] = u + '0';
} else {
a[h - 1][w] = u / 10 + '0';
a[h - 1][w + 1] = u % 10 + '0';
}
a[h - 2][w - 1] = '\\'; a[h - 2][w + 2] = '/';
a[h - 2][w] = a[h - 2][w + 1] = '_';
}
void dfs(int h, int w, int u, int k) {
draw_node(h, w, u);
if (son[u][0] != -1) {
dfs(h - 3 * (1 << k - 1), w - len[k] / 2 - 1, son[u][0], k - 1);
int x = h - 3, y = w - 1;
while (x >= (h - 3 * (1 << k - 1))) {
a[x][y] = '/';
-- x, -- y;
}
}
if (son[u][1] != -1) {
dfs(h - 3 * (1 << k - 1), w + len[k] / 2 + 1, son[u][1], k - 1);
int x = h - 3, y = w + 2;
while (x >= (h - 3 * (1 << k - 1))) {
a[x][y] = '\\';
-- x, ++ y;
}
}
}
void solve() {
int n, k;
std::cin >> n >> k;
for (int i = 1; i <= n; ++ i) {
std::cin >> son[i][0] >> son[i][1];
if (son[i][0] > 0) {
++ in[son[i][0]];
}
if (son[i][1] > 0) {
++ in[son[i][1]];
}
}
len[1] = 4;
for (int i = 2; i <= k; ++ i) {
len[i] = (len[i - 1] + 1) * 2;
}
int w = len[k];
int h = 3 * (1 << k - 1);
int root = 0;
for (int i = 1; i <= n; ++ i) {
if (!in[i]) {
root = i;
break;
}
}
for (int i = 0; i < N; ++ i) {
for (int j = 0; j < N; ++ j) {
a[i][j] = ' ';
}
}
dfs(N - 5, N / 2, root, k - 1);
int u = 0, d = N, l = N, r = 0;
for (int i = 0; i < N; ++ i) {
for (int j = 0; j < N; ++ j) {
if (a[i][j] != ' ') {
u = std::max(u, i);
d = std::min(d, i);
l = std::min(l, j);
r = std::max(r, j);
}
}
}
-- l, ++ r, ++ u, -- d;
for (int i = l; i <= r; ++ i) {
a[u][i] = a[d][i] = '*';
}
for (int i = d; i <= u; ++ i) {
a[i][l] = a[i][r] = '*';
}
for (int i = u; i >= d; -- i) {
for (int j = l; j <= r; ++ j) {
std::cout << a[i][j];
}
std::cout << "\n";
}
}
K. 智乃的逆序数
题意:给你\(n\)个数组,每个数组里的数排序后都是一段连续的数,每个数组的数不重复。要求你用这些数组的数构造一个逆序对为\(k\)的数组,每个数组里的数在新数组里是它的子序列。
因为每个数组的数连续并且不会其他数组有相同的元素,那么这些数组从小到大排序后,逆序对数就等于每个数组的逆序对之和。因为前面数组的每个数一定比后面的任何一个数小,所以只需要计算每个数组的逆序对。然后我们模拟冒泡排序增加逆序对即可,注意不能和同一个数组的数交换。
点击查看代码
void solve() {
int n, k;
std::cin >> n >> k;
std::vector<std::vector<int> > a(n);
for (int i = 0; i < n; ++ i) {
int m;
std::cin >> m;
while (m -- ) {
int x;
std::cin >> x;
a[i].push_back(x);
}
}
std::sort(a.begin(), a.end());
std::vector<std::pair<int, int> > ans;
for (int i = 0; i < n; ++ i) {
for (auto & x : a[i]) {
ans.push_back({x, i});
}
}
n = ans.size();
for (int i = 0; i < n; ++ i) {
for (int j = i + 1; j < n; ++ j) {
if (ans[i].first > ans[j].first) {
-- k;
}
}
}
if (k < 0) {
std::cout << "No\n";
return;
}
for (int i = 0; i < n; ++ i) {
int j = i;
while (j > 0 && k > 0 && ans[j].second != ans[j - 1].second && ans[j].first > ans[j - 1].first) {
-- k;
std::swap(ans[j], ans[j - 1]);
-- j;
}
}
if (k > 0) {
std::cout << "No\n";
return;
}
std::cout << "Yes\n";
for (int i = 0; i < n; ++ i) {
std::cout << ans[i].first << " \n"[i == n - 1];
}
}
L. 智乃的三角遍历
题意:找一条路线恰好经过一次图形的所有边。这个图形是一个\(n\)层的三角形,每一层有\(i\)个三角形。
可以自己找一下方法,也可以打表。
我是每次从\((i, i)\)出发,然后一直到\((n, i)\),再往有走到\((n, i+1)\), 然后往上左右摇摆,走到\((n - 1, i)\) 和\((n - 1, i + 1)\)。最后走到\((i+1, i+1)\)。这样循环一直到\((n,n)\)退出然后一路向上就行。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::cout << "Yes\n";
std::cout << 1 << " ";
int x = 1, k = 1;
for (int i = 1; i <= n; ++ i) {
// std::cout << i << "-----\n";
while (k <= n) {
x += k;
std::cout << x << " ";
k += 1;
}
k = n + 1;
std::cout << x + 1 << " ";
x += 1;
while (k > i + 1) {
std::cout << x - k << " " << x - k + 1 << " ";
x -= k - 1;
-- k;
}
k = i + 1;
// std::cout << "\n";
}
k = n + 1;
while (k > 1) {
x -= k;
std::cout << x << " ";
-- k;
}
}
M. 智乃的牛题
题意:判断给出的字符串能不能组成"\(nowcoder\)"。
排序比较是不是就行,比赛时没看见给出的字符串长度为8,写了个看是不是子序列的代码。
点击查看代码
void solve() {
std::string s;
std::cin >> s;
std::string t = "nowcoder";
std::sort(t.begin(), t.end());
std::sort(s.begin(), s.end());
int j = 0;
for (int i = 0; j < t.size() && i < s.size(); ++ i) {
if (s[i] == t[j]) {
++ j;
}
}
if (j == t.size()) {
std::cout << "happy new year\n";
} else {
std::cout << "I AK IOI\n";
}
}