2588 字
13 分钟
牛客周赛 Round 145
NOTE

比赛链接:牛客周赛 Round 145

A - 小红的好串#

关键词:签到

Code#

// Problem: 小红的好串
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/136016/A
// Time: 2026-05-24 21:32:41
#include <bits/stdc++.h>
using namespace std;
// clang-format off
#define endl '\n'
#define all(x) (x).begin(), (x).end()
#define fastio() ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
// clang-format on
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
using pdd = pair<double, double>;
using pll = pair<long long, long long>;
using i128 = __int128;
const int dx[] = {-1, 0, 1, 0, -1, 1, 1, -1};
const int dy[] = {0, 1, 0, -1, 1, 1, -1, -1};
const int inf = 0x3f3f3f3f;
const int N = 0;
void solve()
{
string s;
cin >> s;
map<char, int> mp;
for (auto c: s)
mp[c]++;
if (mp.size() == 2)
cout << "Yes" << endl;
else
cout << "No" << endl;
}
int main()
{
fastio();
int T = 1;
// cin >> T;
while (T--)
solve();
return 0;
}

B - 小红的好串计数#

关键词:模拟

思路#

正难则反,考虑计算不好的串的贡献。注意到不好的串一定是连续的,计算坏串贡献的表达式与计算全部子串的表达式相同。

Code#

// Problem: 小红的好串计数
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/136016/B
// Time: 2026-05-24 21:34:13
#include <bits/stdc++.h>
using namespace std;
// clang-format off
#define endl '\n'
#define all(x) (x).begin(), (x).end()
#define fastio() ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
// clang-format on
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
using pdd = pair<double, double>;
using pll = pair<long long, long long>;
using i128 = __int128;
const int dx[] = {-1, 0, 1, 0, -1, 1, 1, -1};
const int dy[] = {0, 1, 0, -1, 1, 1, -1, -1};
const int inf = 0x3f3f3f3f;
const int N = 0;
void solve()
{
ll n;
string s;
cin >> n >> s;
ll tot = (n + 1) * n / 2, res = 0;
for (ll i = 0; i < n;)
{
ll j = i;
while (j < n && s[j] == s[i])
j++;
res += (j - i + 1) * (j - i) / 2;
i = j;
}
cout << tot - res << endl;
}
int main()
{
fastio();
int T = 1;
// cin >> T;
while (T--)
solve();
return 0;
}

C - 小红的染色平均数#

关键词:数学

思路#

注意到操作并不改变所有元素的和,根据平均数相等,且目标总和之和等于原数组总和 sumsum:有 S0cnt0=S1cnt1\frac{S_0'}{cnt_0} = \frac{S_1'}{cnt_1}S0+S1=sumS_0' + S_1' = sum,将 S1=sumS0S_1' = sum - S_0' 代入第一个等式,有 S0cnt0=sumS0cnt1\frac{S_0'}{cnt_0} = \frac{sum - S_0'}{cnt_1},移项有 S0(cnt0+cnt1)=sumcnt0S_0' (cnt_0 + cnt_1) = sum \cdot cnt_0S0=sumcnt0nS_0' = \frac{sum \cdot cnt_0}{n},如果 (sum * cnt0) % n != 0,则判定无解。

Code#

// Problem: 小红的染色平均数
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/136016/C
// Time: 2026-05-24 21:42:18
#include <bits/stdc++.h>
using namespace std;
// clang-format off
#define endl '\n'
#define all(x) (x).begin(), (x).end()
#define fastio() ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
// clang-format on
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
using pdd = pair<double, double>;
using pll = pair<long long, long long>;
using i128 = __int128;
const int dx[] = {-1, 0, 1, 0, -1, 1, 1, -1};
const int dy[] = {0, 1, 0, -1, 1, 1, -1, -1};
const int inf = 0x3f3f3f3f;
const int N = 0;
void solve()
{
int n;
cin >> n;
vector<int> a(n);
for (auto &x: a)
cin >> x;
ll sum = accumulate(all(a), 0LL);
string s;
cin >> s;
ll sum0 = 0, sum1 = 0, cnt0 = 0, cnt1 = 0;
for (int i = 0; i < n; i++)
{
if (s[i] == '0')
sum0 += (ll) a[i], cnt0++;
else
sum1 += (ll) a[i], cnt1++;
}
if ((sum * cnt0) % n != 0)
{
cout << "-1" << endl;
return;
}
cout << abs((cnt1 * sum0 - cnt0 * sum1) / (cnt0 + cnt1)) << endl;
}
int main()
{
fastio();
int T = 1;
// cin >> T;
while (T--)
solve();
return 0;
}

D - 小红的排列构造#

关键词:构造

思路#

相比于分配数字,我们考虑分配下标。

既然对于每个位置 ii,都要满足 bi=aib_i = a_ici=aic_i = a_i,这意味着每个下标 ii 都要“分配”给 bb 或者 cc 作为匹配位。我们要确保 bbcc 各自分配到恰好 n2\frac{n}{2} 个下标。

  1. 处理出现 2 次的数字:如果某个数字 xx 在下标 iijj 出现了两次。因为排列里不能有重复数字,所以 bb 只能在其中一个下标匹配 xxcc 在另一个下标匹配 xx

​ 操作:将下标 ii 分配给 bb,下标 jj 分配给 cc

  1. 处理出现 1 次的数字:对于只出现一次的数字,它可以分配给 bb,也可以分配给 cc

​ 操作:我们检查 bb 当前分配到的匹配位数量,如果还不满 n2\frac{n}{2},就分配给 bb;否则全部分配给 cc

  1. 填补空缺,构造完整排列:剩下的 n2\frac{n}{2} 个空位,只要找出 1n1 \sim n还没有被该数组使用的数字,依次填入空位即可。因为填入的是剩余未使用过的数字,所以能保证构成一个合法的排列。

Code#

// Problem: 小红的排列构造
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/136016/D
// Time: 2026-05-24 21:58:45
#include <bits/stdc++.h>
using namespace std;
// clang-format off
#define endl '\n'
#define all(x) (x).begin(), (x).end()
#define fastio() ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
// clang-format on
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
using pdd = pair<double, double>;
using pll = pair<long long, long long>;
using i128 = __int128;
const int dx[] = {-1, 0, 1, 0, -1, 1, 1, -1};
const int dy[] = {0, 1, 0, -1, 1, 1, -1, -1};
const int inf = 0x3f3f3f3f;
const int N = 0;
void solve()
{
int n;
cin >> n;
vector<int> a(n);
vector<int> cnt(n + 1, 0);
vector<vector<int>> pos(n + 1);
for (int i = 0; i < n; i++)
{
cin >> a[i];
cnt[a[i]]++;
pos[a[i]].push_back(i);
}
for (int i = 1; i <= n; i++)
{
if (cnt[i] >= 3)
{
cout << -1 << endl;
return;
}
}
vector<int> b(n, 0), c(n, 0);
vector<bool> stb(n + 1, false), stc(n + 1, false);
int usedb = 0;
for (int i = 1; i <= n; i++)
{
if (cnt[i] == 2)
{
int p1 = pos[i][0], p2 = pos[i][1];
b[p1] = i, stb[i] = true, usedb++;
c[p2] = i, stc[i] = true;
}
}
for (int i = 1; i <= n; i++)
{
if (cnt[i] == 1)
{
int p = pos[i][0];
if (usedb < n / 2)
b[p] = i, stb[i] = true, usedb++;
else
c[p] = i, stc[i] = true;
}
}
vector<int> left_b, left_c;
for (int i = 1; i <= n; i++)
{
if (!stb[i])
left_b.push_back(i);
if (!stc[i])
left_c.push_back(i);
}
int idxb = 0, idxc = 0;
for (int i = 0; i < n; i++)
{
if (b[i] == 0)
b[i] = left_b[idxb++];
if (c[i] == 0)
c[i] = left_c[idxc++];
}
for (auto i: b)
cout << i << " ";
cout << endl;
for (auto i: c)
cout << i << " ";
}
int main()
{
fastio();
int T = 1;
// cin >> T;
while (T--)
solve();
return 0;
}

E - 小红的染色生成树#

关键词:构造,Kruskal

思路#

由于需要恰好出现两次,分别对 0,10, 10,20, 21,21, 2KruskalKruskal 即可。

每个 KruskalKruskal 的过程需要扫两遍,第一遍强行挑出第一条颜色为 A 的边第一条颜色为 B 的边加入并查集。这样就保证了如果能生成树,里面必定“恰好包含这两种颜色”;扫完第一遍后,假如一条 aa 或一条 bb 都找不到,就直接返回 false。第二遍正常扫,把树补全。最后检查并查集中所有点是否连通。

Code#

// Problem: 小红的染色生成树
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/134981/E
// Time: 2026-05-25 19:13:36
#include <bits/stdc++.h>
using namespace std;
// clang-format off
#define endl '\n'
#define all(x) (x).begin(), (x).end()
#define fastio() ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
// clang-format on
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
using pdd = pair<double, double>;
using pll = pair<long long, long long>;
using i128 = __int128;
const int dx[] = {-1, 0, 1, 0, -1, 1, 1, -1};
const int dy[] = {0, 1, 0, -1, 1, 1, -1, -1};
const int inf = 0x3f3f3f3f;
const int N = 2e5 + 10;
struct Edge
{
int u, v, w;
} edges[N];
struct DSU
{
vector<int> parent;
vector<int> sz;
int comp_cnt;
DSU(int n)
{
parent.resize(n + 1);
iota(parent.begin(), parent.end(), 0);
sz.assign(n + 1, 1);
comp_cnt = n;
}
int find(int i)
{
if (parent[i] == i)
return i;
return parent[i] = find(parent[i]);
}
bool unite(int i, int j)
{
int pi = find(i);
int pj = find(j);
if (pi != pj)
{
if (sz[pi] < sz[pj])
swap(pi, pj);
parent[pj] = pi;
sz[pi] += sz[pj];
comp_cnt--;
return true;
}
return false;
}
};
void solve()
{
int n, m;
cin >> n >> m;
for (int i = 0; i < m; i++)
cin >> edges[i].u >> edges[i].v >> edges[i].w;
auto Kruskal = [&](int a, int b) -> bool
{
DSU dsu(n);
vector<pii> res;
bool f1 = false, f2 = false;
for (int i = 0; i < m; i++)
{
int u = edges[i].u, v = edges[i].v, w = edges[i].w;
if (w == a && !f1)
{
dsu.unite(u, v);
res.push_back({u, v});
f1 = true;
}
else if (w == b && !f2)
{
dsu.unite(u, v);
res.push_back({u, v});
f2 = true;
}
}
if (!f1 || !f2)
return false;
for (int i = 0; i < m; i++)
{
int u = edges[i].u, v = edges[i].v, w = edges[i].w;
if (w != a && w != b)
continue;
if (dsu.unite(u, v))
res.push_back({u, v});
}
if (dsu.comp_cnt == 1)
{
for (auto &[u, v]: res)
cout << u << " " << v << endl;
return true;
}
return false;
};
if (!Kruskal(0, 1) && !Kruskal(0, 2) && !Kruskal(1, 2))
cout << -1 << endl;
}
int main()
{
fastio();
int T = 1;
// cin >> T;
while (T--)
solve();
return 0;
}

F - 小红的好串构造#

关键词:构造,数学

思路#

为了让 kk 的计算最简单,我们考虑一种构造方法:让夹在中间的字符隔断两边的计算。

aax 个+b+a+ccz 个\underbrace{a \dots a}_{x \text{ 个}} + \mathbf{b} + \mathbf{a} + \underbrace{c \dots c}_{z \text{ 个}}

在这个字符串中,一共只有三种字符 a, b, c。我们来统计它的好串数量:

  1. ab 的好串:必须包含 b。左端点可以在前面的 xxa 或是 b 自己身上(共 x+1x+1 种选择),右端点可以是 b 自己或是后面紧跟的那个 a(共 2 种选择)。减去只有 b 自己的一种情况。总数为 2(x+1)1=2x+12(x+1) - 1 = \mathbf{2x + 1}
  2. ac 的好串:不能包含 b。所以左端点只能是中间那个被 bc 夹着的孤独的 a(1 种选择)。右端点落在后面 zzc 的任意位置。总数为 z\mathbf{z}
  3. bc 的好串:只要同时包含 bc,就必定会跨过中间的那个 a,导致含有 3 种字符,全部不合法,总数为 0\mathbf{0}

将它们相加,总好串数量 Sum=2x+1+zSum = 2x + 1 + z。又因为字符串总长度 n=x+1+1+z    z=nx2n = x + 1 + 1 + z \implies z = n - x - 2

代入得到:Sum=2x+1+(nx2)=n+x1Sum = 2x + 1 + (n - x - 2) = \mathbf{n + x - 1}。根据已知条件,Sum=kSum = k

且由于题目约束:n1k2n4n-1 \le k \le 2n-4,最小值 k=n1    x=0k = n-1 \implies x = 0,最大值 k=2n4    x=n3    z=1k = 2n-4 \implies x = n-3 \implies z = 1,因此 xxzz 永远是合法的非负数。

Code#

// Problem: 小红的好串构造
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/134981/F
// Time: 2026-05-25 20:10:20
#include <bits/stdc++.h>
using namespace std;
// clang-format off
#define endl '\n'
#define all(x) (x).begin(), (x).end()
#define fastio() ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
// clang-format on
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
using pdd = pair<double, double>;
using pll = pair<long long, long long>;
using i128 = __int128;
const int dx[] = {-1, 0, 1, 0, -1, 1, 1, -1};
const int dy[] = {0, 1, 0, -1, 1, 1, -1, -1};
const int inf = 0x3f3f3f3f;
const int N = 0;
void solve()
{
ll n, k;
cin >> n >> k;
ll x = k - n + 1;
ll z = n - 2 - x;
// x 个 'a' + "ba" + z 个 'c'
string res = string(x, 'a') + "ba" + string(z, 'c');
cout << res << endl;
}
int main()
{
fastio();
int T = 1;
cin >> T;
while (T--)
solve();
return 0;
}